diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..afac5a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +build/* +website_meta_data.txt +website_analytics_data.txt +website_script_data.txt +package-lock.json +node_modules +__pycache__ diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..ec9cff1 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,39 @@ +{ + "esversion" : 6, + "curly" : true, + "forin" : true, + "latedef" : true, + "nonew" : true, + "quotmark" : true, + "funcscope" : true, + "lastsemic" : true, + "loopfunc" : true, + "shadow" : false, + "undef" : true, + "unused" : false, + "bitwise" : false, + "freeze" : true, + "leanswitch" : true, + "eqeqeq" : true, + "futurehostile" : true, + "regexpu" : true, + "varstmt" : true, + "globals" : { + "OV" : true, + "THREE" : false, + "TextDecoder" : false, + "XMLHttpRequest" : false, + "FileReader" : false, + "Blob" : false, + "URL" : false, + "window" : false, + "document" : false, + "setTimeout" : false, + "requestAnimationFrame" : false, + "getComputedStyle" : false, + "alert" : false, + "atob" : false, + "console" : false, + "$" : false + } +} diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..8e84f9c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Viktor Kovacs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..98e56f9 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Online 3D Viewer + +This repository contains the source code of the https://3dviewer.net website. The website can open several 3D file formats and visualize the model in your browser. + +## Supported file formats + +### Import + +- obj (with mtl and texture) +- 3ds (with texture) +- stl (ascii and binary) +- ply (ascii and binary) +- gltf (ascii and binary) +- off (ascii only) + +### Export + +- obj (with mtl) +- stl (ascii and binary) +- ply (ascii and binary) +- off (ascii only) + +## Features + +- Load model: + - Select files from a file browser dialog + - Drag and drop files from your computer + - Specify files by web url + - Specify files by web url in hash parameters +- Explore model: + - Orbit, pan, zoom + - Set up direction + - Fit to window +- Investigate model: + - List used and missing files + - List all materials and meshes + - Show/hide and zoom to a specific mesh + - List materials used by a specific mesh + - Show model information (model size, vertex and polygon count) +- Export model to various format +- Embed viewer in your website diff --git a/StartServer.bat b/StartServer.bat new file mode 100644 index 0000000..69a4f71 --- /dev/null +++ b/StartServer.bat @@ -0,0 +1 @@ +python -m http.server 8000 diff --git a/libs/jquery-3.5.1.min.js b/libs/jquery-3.5.1.min.js new file mode 100644 index 0000000..b061403 --- /dev/null +++ b/libs/jquery-3.5.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0=0;--a){var o=this.tryEntries[a],s=o.completion;if("root"===o.tryLoc)return i("end");if(o.tryLoc<=this.prev){var l=r.call(o,"catchLoc"),c=r.call(o,"finallyLoc");if(l&&c){if(this.prev=0;--n){var i=this.tryEntries[n];if(i.tryLoc<=this.prev&&r.call(i,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),A(n),m}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var r=n.completion;if("throw"===r.type){var i=r.arg;A(n)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,n,r){return this.delegate={iterator:R(t),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=e),m}},t}("object"==typeof module?module.exports:{});try{regeneratorRuntime=e}catch(t){Function("r","regeneratorRuntime = r")(e)}var n="126",r=100,i=300,a=301,o=302,s=303,l=304,c=306,u=307,h=1e3,d=1001,p=1002,f=1003,m=1004,v=1005,g=1006,y=1007,x=1008,_=1009,b=1012,w=1014,M=1015,S=1016,T=1020,E=1022,A=1023,L=1026,R=1027,C=33776,P=33777,D=33778,I=33779,N=35840,B=35841,O=35842,z=35843,F=37492,H=37496,G=2300,U=2301,k=2302,V=2400,W=2401,j=2402,q=2500,X=2501,Y=3e3,Z=3001,J=3007,Q=3002,K=3004,$=3005,tt=3006,et=7680,nt=35044,rt=35048,it="300 es";function at(t,e,n,r,i,a,o){try{var s=t[a](o),l=s.value}catch(t){return void n(t)}s.done?e(l):Promise.resolve(l).then(r,i)}function ot(t,e){for(var n=0;nt.length)&&(e=t.length);for(var n=0,r=new Array(e);n=t.length?{done:!0}:{done:!1,value:t[r++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=t[Symbol.iterator]()).next.bind(n)}function pt(){}Object.assign(pt.prototype,{addEventListener:function(t,e){void 0===this._listeners&&(this._listeners={});var n=this._listeners;void 0===n[t]&&(n[t]=[]),-1===n[t].indexOf(e)&&n[t].push(e)},hasEventListener:function(t,e){if(void 0===this._listeners)return!1;var n=this._listeners;return void 0!==n[t]&&-1!==n[t].indexOf(e)},removeEventListener:function(t,e){if(void 0!==this._listeners){var n=this._listeners[t];if(void 0!==n){var r=n.indexOf(e);-1!==r&&n.splice(r,1)}}},dispatchEvent:function(t){if(void 0!==this._listeners){var e=this._listeners[t.type];if(void 0!==e){t.target=this;for(var n=e.slice(0),r=0,i=n.length;r>8&255]+ft[t>>16&255]+ft[t>>24&255]+"-"+ft[255&e]+ft[e>>8&255]+"-"+ft[e>>16&15|64]+ft[e>>24&255]+"-"+ft[63&n|128]+ft[n>>8&255]+"-"+ft[n>>16&255]+ft[n>>24&255]+ft[255&r]+ft[r>>8&255]+ft[r>>16&255]+ft[r>>24&255]).toUpperCase()},clamp:function(t,e,n){return Math.max(e,Math.min(n,t))},euclideanModulo:function(t,e){return(t%e+e)%e},mapLinear:function(t,e,n,r,i){return r+(t-e)*(i-r)/(n-e)},lerp:function(t,e,n){return(1-n)*t+n*e},damp:function(t,e,n,r){return gt.lerp(t,e,1-Math.exp(-n*r))},pingpong:function(t,e){return void 0===e&&(e=1),e-Math.abs(gt.euclideanModulo(t,2*e)-e)},smoothstep:function(t,e,n){return t<=e?0:t>=n?1:(t=(t-e)/(n-e))*t*(3-2*t)},smootherstep:function(t,e,n){return t<=e?0:t>=n?1:(t=(t-e)/(n-e))*t*t*(t*(6*t-15)+10)},randInt:function(t,e){return t+Math.floor(Math.random()*(e-t+1))},randFloat:function(t,e){return t+Math.random()*(e-t)},randFloatSpread:function(t){return t*(.5-Math.random())},seededRandom:function(t){return void 0!==t&&(vt=t%2147483647),((vt=16807*vt%2147483647)-1)/2147483646},degToRad:function(t){return t*gt.DEG2RAD},radToDeg:function(t){return t*gt.RAD2DEG},isPowerOfTwo:function(t){return 0==(t&t-1)&&0!==t},ceilPowerOfTwo:function(t){return Math.pow(2,Math.ceil(Math.log(t)/Math.LN2))},floorPowerOfTwo:function(t){return Math.pow(2,Math.floor(Math.log(t)/Math.LN2))},setQuaternionFromProperEuler:function(t,e,n,r,i){var a=Math.cos,o=Math.sin,s=a(n/2),l=o(n/2),c=a((e+r)/2),u=o((e+r)/2),h=a((e-r)/2),d=o((e-r)/2),p=a((r-e)/2),f=o((r-e)/2);switch(i){case"XYX":t.set(s*u,l*h,l*d,s*c);break;case"YZY":t.set(l*d,s*u,l*h,s*c);break;case"ZXZ":t.set(l*h,l*d,s*u,s*c);break;case"XZX":t.set(s*u,l*f,l*p,s*c);break;case"YXY":t.set(l*p,s*u,l*f,s*c);break;case"ZYZ":t.set(l*f,l*p,s*u,s*c);break;default:console.warn("THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: "+i)}}},yt=function(){function t(t,e){void 0===t&&(t=0),void 0===e&&(e=0),this.x=t,this.y=e}var e=t.prototype;return e.set=function(t,e){return this.x=t,this.y=e,this},e.setScalar=function(t){return this.x=t,this.y=t,this},e.setX=function(t){return this.x=t,this},e.setY=function(t){return this.y=t,this},e.setComponent=function(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;default:throw new Error("index is out of range: "+t)}return this},e.getComponent=function(t){switch(t){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+t)}},e.clone=function(){return new this.constructor(this.x,this.y)},e.copy=function(t){return this.x=t.x,this.y=t.y,this},e.add=function(t,e){return void 0!==e?(console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(t,e)):(this.x+=t.x,this.y+=t.y,this)},e.addScalar=function(t){return this.x+=t,this.y+=t,this},e.addVectors=function(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this},e.addScaledVector=function(t,e){return this.x+=t.x*e,this.y+=t.y*e,this},e.sub=function(t,e){return void 0!==e?(console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(t,e)):(this.x-=t.x,this.y-=t.y,this)},e.subScalar=function(t){return this.x-=t,this.y-=t,this},e.subVectors=function(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this},e.multiply=function(t){return this.x*=t.x,this.y*=t.y,this},e.multiplyScalar=function(t){return this.x*=t,this.y*=t,this},e.divide=function(t){return this.x/=t.x,this.y/=t.y,this},e.divideScalar=function(t){return this.multiplyScalar(1/t)},e.applyMatrix3=function(t){var e=this.x,n=this.y,r=t.elements;return this.x=r[0]*e+r[3]*n+r[6],this.y=r[1]*e+r[4]*n+r[7],this},e.min=function(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this},e.max=function(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this},e.clamp=function(t,e){return this.x=Math.max(t.x,Math.min(e.x,this.x)),this.y=Math.max(t.y,Math.min(e.y,this.y)),this},e.clampScalar=function(t,e){return this.x=Math.max(t,Math.min(e,this.x)),this.y=Math.max(t,Math.min(e,this.y)),this},e.clampLength=function(t,e){var n=this.length();return this.divideScalar(n||1).multiplyScalar(Math.max(t,Math.min(e,n)))},e.floor=function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},e.ceil=function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},e.round=function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},e.roundToZero=function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this},e.negate=function(){return this.x=-this.x,this.y=-this.y,this},e.dot=function(t){return this.x*t.x+this.y*t.y},e.cross=function(t){return this.x*t.y-this.y*t.x},e.lengthSq=function(){return this.x*this.x+this.y*this.y},e.length=function(){return Math.sqrt(this.x*this.x+this.y*this.y)},e.manhattanLength=function(){return Math.abs(this.x)+Math.abs(this.y)},e.normalize=function(){return this.divideScalar(this.length()||1)},e.angle=function(){var t=Math.atan2(-this.y,-this.x)+Math.PI;return t},e.distanceTo=function(t){return Math.sqrt(this.distanceToSquared(t))},e.distanceToSquared=function(t){var e=this.x-t.x,n=this.y-t.y;return e*e+n*n},e.manhattanDistanceTo=function(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)},e.setLength=function(t){return this.normalize().multiplyScalar(t)},e.lerp=function(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this},e.lerpVectors=function(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this},e.equals=function(t){return t.x===this.x&&t.y===this.y},e.fromArray=function(t,e){return void 0===e&&(e=0),this.x=t[e],this.y=t[e+1],this},e.toArray=function(t,e){return void 0===t&&(t=[]),void 0===e&&(e=0),t[e]=this.x,t[e+1]=this.y,t},e.fromBufferAttribute=function(t,e,n){return void 0!==n&&console.warn("THREE.Vector2: offset has been removed from .fromBufferAttribute()."),this.x=t.getX(e),this.y=t.getY(e),this},e.rotateAround=function(t,e){var n=Math.cos(e),r=Math.sin(e),i=this.x-t.x,a=this.y-t.y;return this.x=i*n-a*r+t.x,this.y=i*r+a*n+t.y,this},e.random=function(){return this.x=Math.random(),this.y=Math.random(),this},st(t,[{key:"width",get:function(){return this.x},set:function(t){this.x=t}},{key:"height",get:function(){return this.y},set:function(t){this.y=t}}]),t}();yt.prototype.isVector2=!0;var xt,_t=function(){function t(){this.elements=[1,0,0,0,1,0,0,0,1],arguments.length>0&&console.error("THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.")}var e=t.prototype;return e.set=function(t,e,n,r,i,a,o,s,l){var c=this.elements;return c[0]=t,c[1]=r,c[2]=o,c[3]=e,c[4]=i,c[5]=s,c[6]=n,c[7]=a,c[8]=l,this},e.identity=function(){return this.set(1,0,0,0,1,0,0,0,1),this},e.copy=function(t){var e=this.elements,n=t.elements;return e[0]=n[0],e[1]=n[1],e[2]=n[2],e[3]=n[3],e[4]=n[4],e[5]=n[5],e[6]=n[6],e[7]=n[7],e[8]=n[8],this},e.extractBasis=function(t,e,n){return t.setFromMatrix3Column(this,0),e.setFromMatrix3Column(this,1),n.setFromMatrix3Column(this,2),this},e.setFromMatrix4=function(t){var e=t.elements;return this.set(e[0],e[4],e[8],e[1],e[5],e[9],e[2],e[6],e[10]),this},e.multiply=function(t){return this.multiplyMatrices(this,t)},e.premultiply=function(t){return this.multiplyMatrices(t,this)},e.multiplyMatrices=function(t,e){var n=t.elements,r=e.elements,i=this.elements,a=n[0],o=n[3],s=n[6],l=n[1],c=n[4],u=n[7],h=n[2],d=n[5],p=n[8],f=r[0],m=r[3],v=r[6],g=r[1],y=r[4],x=r[7],_=r[2],b=r[5],w=r[8];return i[0]=a*f+o*g+s*_,i[3]=a*m+o*y+s*b,i[6]=a*v+o*x+s*w,i[1]=l*f+c*g+u*_,i[4]=l*m+c*y+u*b,i[7]=l*v+c*x+u*w,i[2]=h*f+d*g+p*_,i[5]=h*m+d*y+p*b,i[8]=h*v+d*x+p*w,this},e.multiplyScalar=function(t){var e=this.elements;return e[0]*=t,e[3]*=t,e[6]*=t,e[1]*=t,e[4]*=t,e[7]*=t,e[2]*=t,e[5]*=t,e[8]*=t,this},e.determinant=function(){var t=this.elements,e=t[0],n=t[1],r=t[2],i=t[3],a=t[4],o=t[5],s=t[6],l=t[7],c=t[8];return e*a*c-e*o*l-n*i*c+n*o*s+r*i*l-r*a*s},e.invert=function(){var t=this.elements,e=t[0],n=t[1],r=t[2],i=t[3],a=t[4],o=t[5],s=t[6],l=t[7],c=t[8],u=c*a-o*l,h=o*s-c*i,d=l*i-a*s,p=e*u+n*h+r*d;if(0===p)return this.set(0,0,0,0,0,0,0,0,0);var f=1/p;return t[0]=u*f,t[1]=(r*l-c*n)*f,t[2]=(o*n-r*a)*f,t[3]=h*f,t[4]=(c*e-r*s)*f,t[5]=(r*i-o*e)*f,t[6]=d*f,t[7]=(n*s-l*e)*f,t[8]=(a*e-n*i)*f,this},e.transpose=function(){var t,e=this.elements;return t=e[1],e[1]=e[3],e[3]=t,t=e[2],e[2]=e[6],e[6]=t,t=e[5],e[5]=e[7],e[7]=t,this},e.getNormalMatrix=function(t){return this.setFromMatrix4(t).invert().transpose()},e.transposeIntoArray=function(t){var e=this.elements;return t[0]=e[0],t[1]=e[3],t[2]=e[6],t[3]=e[1],t[4]=e[4],t[5]=e[7],t[6]=e[2],t[7]=e[5],t[8]=e[8],this},e.setUvTransform=function(t,e,n,r,i,a,o){var s=Math.cos(i),l=Math.sin(i);return this.set(n*s,n*l,-n*(s*a+l*o)+a+t,-r*l,r*s,-r*(-l*a+s*o)+o+e,0,0,1),this},e.scale=function(t,e){var n=this.elements;return n[0]*=t,n[3]*=t,n[6]*=t,n[1]*=e,n[4]*=e,n[7]*=e,this},e.rotate=function(t){var e=Math.cos(t),n=Math.sin(t),r=this.elements,i=r[0],a=r[3],o=r[6],s=r[1],l=r[4],c=r[7];return r[0]=e*i+n*s,r[3]=e*a+n*l,r[6]=e*o+n*c,r[1]=-n*i+e*s,r[4]=-n*a+e*l,r[7]=-n*o+e*c,this},e.translate=function(t,e){var n=this.elements;return n[0]+=t*n[2],n[3]+=t*n[5],n[6]+=t*n[8],n[1]+=e*n[2],n[4]+=e*n[5],n[7]+=e*n[8],this},e.equals=function(t){for(var e=this.elements,n=t.elements,r=0;r<9;r++)if(e[r]!==n[r])return!1;return!0},e.fromArray=function(t,e){void 0===e&&(e=0);for(var n=0;n<9;n++)this.elements[n]=t[n+e];return this},e.toArray=function(t,e){void 0===t&&(t=[]),void 0===e&&(e=0);var n=this.elements;return t[e]=n[0],t[e+1]=n[1],t[e+2]=n[2],t[e+3]=n[3],t[e+4]=n[4],t[e+5]=n[5],t[e+6]=n[6],t[e+7]=n[7],t[e+8]=n[8],t},e.clone=function(){return(new this.constructor).fromArray(this.elements)},t}();_t.prototype.isMatrix3=!0;var bt={getDataURL:function(t){if(/^data:/i.test(t.src))return t.src;if("undefined"==typeof HTMLCanvasElement)return t.src;var e;if(t instanceof HTMLCanvasElement)e=t;else{void 0===xt&&(xt=document.createElementNS("http://www.w3.org/1999/xhtml","canvas")),xt.width=t.width,xt.height=t.height;var n=xt.getContext("2d");t instanceof ImageData?n.putImageData(t,0,0):n.drawImage(t,0,0,t.width,t.height),e=xt}return e.width>2048||e.height>2048?e.toDataURL("image/jpeg",.6):e.toDataURL("image/png")}},wt=0,Mt=function(t){function e(n,r,i,a,o,s,l,c,u,h){var p;return void 0===n&&(n=e.DEFAULT_IMAGE),void 0===r&&(r=e.DEFAULT_MAPPING),void 0===i&&(i=d),void 0===a&&(a=d),void 0===o&&(o=g),void 0===s&&(s=x),void 0===l&&(l=A),void 0===c&&(c=_),void 0===u&&(u=1),void 0===h&&(h=Y),p=t.call(this)||this,Object.defineProperty(ut(p),"id",{value:wt++}),p.uuid=gt.generateUUID(),p.name="",p.image=n,p.mipmaps=[],p.mapping=r,p.wrapS=i,p.wrapT=a,p.magFilter=o,p.minFilter=s,p.anisotropy=u,p.format=l,p.internalFormat=null,p.type=c,p.offset=new yt(0,0),p.repeat=new yt(1,1),p.center=new yt(0,0),p.rotation=0,p.matrixAutoUpdate=!0,p.matrix=new _t,p.generateMipmaps=!0,p.premultiplyAlpha=!1,p.flipY=!0,p.unpackAlignment=4,p.encoding=h,p.version=0,p.onUpdate=null,p}lt(e,t);var n=e.prototype;return n.updateMatrix=function(){this.matrix.setUvTransform(this.offset.x,this.offset.y,this.repeat.x,this.repeat.y,this.rotation,this.center.x,this.center.y)},n.clone=function(){return(new this.constructor).copy(this)},n.copy=function(t){return this.name=t.name,this.image=t.image,this.mipmaps=t.mipmaps.slice(0),this.mapping=t.mapping,this.wrapS=t.wrapS,this.wrapT=t.wrapT,this.magFilter=t.magFilter,this.minFilter=t.minFilter,this.anisotropy=t.anisotropy,this.format=t.format,this.internalFormat=t.internalFormat,this.type=t.type,this.offset.copy(t.offset),this.repeat.copy(t.repeat),this.center.copy(t.center),this.rotation=t.rotation,this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrix.copy(t.matrix),this.generateMipmaps=t.generateMipmaps,this.premultiplyAlpha=t.premultiplyAlpha,this.flipY=t.flipY,this.unpackAlignment=t.unpackAlignment,this.encoding=t.encoding,this},n.toJSON=function(t){var e=void 0===t||"string"==typeof t;if(!e&&void 0!==t.textures[this.uuid])return t.textures[this.uuid];var n={metadata:{version:4.5,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,mapping:this.mapping,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],center:[this.center.x,this.center.y],rotation:this.rotation,wrap:[this.wrapS,this.wrapT],format:this.format,type:this.type,encoding:this.encoding,minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy,flipY:this.flipY,premultiplyAlpha:this.premultiplyAlpha,unpackAlignment:this.unpackAlignment};if(void 0!==this.image){var r=this.image;if(void 0===r.uuid&&(r.uuid=gt.generateUUID()),!e&&void 0===t.images[r.uuid]){var i;if(Array.isArray(r)){i=[];for(var a=0,o=r.length;a1)switch(this.wrapS){case h:t.x=t.x-Math.floor(t.x);break;case d:t.x=t.x<0?0:1;break;case p:1===Math.abs(Math.floor(t.x)%2)?t.x=Math.ceil(t.x)-t.x:t.x=t.x-Math.floor(t.x)}if(t.y<0||t.y>1)switch(this.wrapT){case h:t.y=t.y-Math.floor(t.y);break;case d:t.y=t.y<0?0:1;break;case p:1===Math.abs(Math.floor(t.y)%2)?t.y=Math.ceil(t.y)-t.y:t.y=t.y-Math.floor(t.y)}return this.flipY&&(t.y=1-t.y),t},st(e,[{key:"needsUpdate",set:function(t){!0===t&&this.version++}}]),e}(pt);function St(t){return"undefined"!=typeof HTMLImageElement&&t instanceof HTMLImageElement||"undefined"!=typeof HTMLCanvasElement&&t instanceof HTMLCanvasElement||"undefined"!=typeof ImageBitmap&&t instanceof ImageBitmap?bt.getDataURL(t):t.data?{data:Array.prototype.slice.call(t.data),width:t.width,height:t.height,type:t.data.constructor.name}:(console.warn("THREE.Texture: Unable to serialize Texture."),{})}Mt.DEFAULT_IMAGE=void 0,Mt.DEFAULT_MAPPING=i,Mt.prototype.isTexture=!0;var Tt=function(){function t(t,e,n,r){void 0===t&&(t=0),void 0===e&&(e=0),void 0===n&&(n=0),void 0===r&&(r=1),this.x=t,this.y=e,this.z=n,this.w=r}var e=t.prototype;return e.set=function(t,e,n,r){return this.x=t,this.y=e,this.z=n,this.w=r,this},e.setScalar=function(t){return this.x=t,this.y=t,this.z=t,this.w=t,this},e.setX=function(t){return this.x=t,this},e.setY=function(t){return this.y=t,this},e.setZ=function(t){return this.z=t,this},e.setW=function(t){return this.w=t,this},e.setComponent=function(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;case 3:this.w=e;break;default:throw new Error("index is out of range: "+t)}return this},e.getComponent=function(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+t)}},e.clone=function(){return new this.constructor(this.x,this.y,this.z,this.w)},e.copy=function(t){return this.x=t.x,this.y=t.y,this.z=t.z,this.w=void 0!==t.w?t.w:1,this},e.add=function(t,e){return void 0!==e?(console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(t,e)):(this.x+=t.x,this.y+=t.y,this.z+=t.z,this.w+=t.w,this)},e.addScalar=function(t){return this.x+=t,this.y+=t,this.z+=t,this.w+=t,this},e.addVectors=function(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this.w=t.w+e.w,this},e.addScaledVector=function(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this.w+=t.w*e,this},e.sub=function(t,e){return void 0!==e?(console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(t,e)):(this.x-=t.x,this.y-=t.y,this.z-=t.z,this.w-=t.w,this)},e.subScalar=function(t){return this.x-=t,this.y-=t,this.z-=t,this.w-=t,this},e.subVectors=function(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this.w=t.w-e.w,this},e.multiply=function(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this.w*=t.w,this},e.multiplyScalar=function(t){return this.x*=t,this.y*=t,this.z*=t,this.w*=t,this},e.applyMatrix4=function(t){var e=this.x,n=this.y,r=this.z,i=this.w,a=t.elements;return this.x=a[0]*e+a[4]*n+a[8]*r+a[12]*i,this.y=a[1]*e+a[5]*n+a[9]*r+a[13]*i,this.z=a[2]*e+a[6]*n+a[10]*r+a[14]*i,this.w=a[3]*e+a[7]*n+a[11]*r+a[15]*i,this},e.divideScalar=function(t){return this.multiplyScalar(1/t)},e.setAxisAngleFromQuaternion=function(t){this.w=2*Math.acos(t.w);var e=Math.sqrt(1-t.w*t.w);return e<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=t.x/e,this.y=t.y/e,this.z=t.z/e),this},e.setAxisAngleFromRotationMatrix=function(t){var e,n,r,i,a=.01,o=.1,s=t.elements,l=s[0],c=s[4],u=s[8],h=s[1],d=s[5],p=s[9],f=s[2],m=s[6],v=s[10];if(Math.abs(c-h)y&&g>x?gx?y=0?1:-1,y=1-v*v;if(y>Number.EPSILON){var x=Math.sqrt(y),_=Math.atan2(x,v*g);m=Math.sin(m*_)/x,o=Math.sin(o*_)/x}var b=o*g;if(s=s*m+h*b,l=l*m+d*b,c=c*m+p*b,u=u*m+f*b,m===1-o){var w=1/Math.sqrt(s*s+l*l+c*c+u*u);s*=w,l*=w,c*=w,u*=w}}t[e]=s,t[e+1]=l,t[e+2]=c,t[e+3]=u},t.multiplyQuaternionsFlat=function(t,e,n,r,i,a){var o=n[r],s=n[r+1],l=n[r+2],c=n[r+3],u=i[a],h=i[a+1],d=i[a+2],p=i[a+3];return t[e]=o*p+c*u+s*d-l*h,t[e+1]=s*p+c*h+l*u-o*d,t[e+2]=l*p+c*d+o*h-s*u,t[e+3]=c*p-o*u-s*h-l*d,t};var e=t.prototype;return e.set=function(t,e,n,r){return this._x=t,this._y=e,this._z=n,this._w=r,this._onChangeCallback(),this},e.clone=function(){return new this.constructor(this._x,this._y,this._z,this._w)},e.copy=function(t){return this._x=t.x,this._y=t.y,this._z=t.z,this._w=t.w,this._onChangeCallback(),this},e.setFromEuler=function(t,e){if(!t||!t.isEuler)throw new Error("THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.");var n=t._x,r=t._y,i=t._z,a=t._order,o=Math.cos,s=Math.sin,l=o(n/2),c=o(r/2),u=o(i/2),h=s(n/2),d=s(r/2),p=s(i/2);switch(a){case"XYZ":this._x=h*c*u+l*d*p,this._y=l*d*u-h*c*p,this._z=l*c*p+h*d*u,this._w=l*c*u-h*d*p;break;case"YXZ":this._x=h*c*u+l*d*p,this._y=l*d*u-h*c*p,this._z=l*c*p-h*d*u,this._w=l*c*u+h*d*p;break;case"ZXY":this._x=h*c*u-l*d*p,this._y=l*d*u+h*c*p,this._z=l*c*p+h*d*u,this._w=l*c*u-h*d*p;break;case"ZYX":this._x=h*c*u-l*d*p,this._y=l*d*u+h*c*p,this._z=l*c*p-h*d*u,this._w=l*c*u+h*d*p;break;case"YZX":this._x=h*c*u+l*d*p,this._y=l*d*u+h*c*p,this._z=l*c*p-h*d*u,this._w=l*c*u-h*d*p;break;case"XZY":this._x=h*c*u-l*d*p,this._y=l*d*u-h*c*p,this._z=l*c*p+h*d*u,this._w=l*c*u+h*d*p;break;default:console.warn("THREE.Quaternion: .setFromEuler() encountered an unknown order: "+a)}return!1!==e&&this._onChangeCallback(),this},e.setFromAxisAngle=function(t,e){var n=e/2,r=Math.sin(n);return this._x=t.x*r,this._y=t.y*r,this._z=t.z*r,this._w=Math.cos(n),this._onChangeCallback(),this},e.setFromRotationMatrix=function(t){var e=t.elements,n=e[0],r=e[4],i=e[8],a=e[1],o=e[5],s=e[9],l=e[2],c=e[6],u=e[10],h=n+o+u;if(h>0){var d=.5/Math.sqrt(h+1);this._w=.25/d,this._x=(c-s)*d,this._y=(i-l)*d,this._z=(a-r)*d}else if(n>o&&n>u){var p=2*Math.sqrt(1+n-o-u);this._w=(c-s)/p,this._x=.25*p,this._y=(r+a)/p,this._z=(i+l)/p}else if(o>u){var f=2*Math.sqrt(1+o-n-u);this._w=(i-l)/f,this._x=(r+a)/f,this._y=.25*f,this._z=(s+c)/f}else{var m=2*Math.sqrt(1+u-n-o);this._w=(a-r)/m,this._x=(i+l)/m,this._y=(s+c)/m,this._z=.25*m}return this._onChangeCallback(),this},e.setFromUnitVectors=function(t,e){var n=t.dot(e)+1;return n<1e-6?(n=0,Math.abs(t.x)>Math.abs(t.z)?(this._x=-t.y,this._y=t.x,this._z=0,this._w=n):(this._x=0,this._y=-t.z,this._z=t.y,this._w=n)):(this._x=t.y*e.z-t.z*e.y,this._y=t.z*e.x-t.x*e.z,this._z=t.x*e.y-t.y*e.x,this._w=n),this.normalize()},e.angleTo=function(t){return 2*Math.acos(Math.abs(gt.clamp(this.dot(t),-1,1)))},e.rotateTowards=function(t,e){var n=this.angleTo(t);if(0===n)return this;var r=Math.min(1,e/n);return this.slerp(t,r),this},e.identity=function(){return this.set(0,0,0,1)},e.invert=function(){return this.conjugate()},e.conjugate=function(){return this._x*=-1,this._y*=-1,this._z*=-1,this._onChangeCallback(),this},e.dot=function(t){return this._x*t._x+this._y*t._y+this._z*t._z+this._w*t._w},e.lengthSq=function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},e.length=function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},e.normalize=function(){var t=this.length();return 0===t?(this._x=0,this._y=0,this._z=0,this._w=1):(t=1/t,this._x=this._x*t,this._y=this._y*t,this._z=this._z*t,this._w=this._w*t),this._onChangeCallback(),this},e.multiply=function(t,e){return void 0!==e?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(t,e)):this.multiplyQuaternions(this,t)},e.premultiply=function(t){return this.multiplyQuaternions(t,this)},e.multiplyQuaternions=function(t,e){var n=t._x,r=t._y,i=t._z,a=t._w,o=e._x,s=e._y,l=e._z,c=e._w;return this._x=n*c+a*o+r*l-i*s,this._y=r*c+a*s+i*o-n*l,this._z=i*c+a*l+n*s-r*o,this._w=a*c-n*o-r*s-i*l,this._onChangeCallback(),this},e.slerp=function(t,e){if(0===e)return this;if(1===e)return this.copy(t);var n=this._x,r=this._y,i=this._z,a=this._w,o=a*t._w+n*t._x+r*t._y+i*t._z;if(o<0?(this._w=-t._w,this._x=-t._x,this._y=-t._y,this._z=-t._z,o=-o):this.copy(t),o>=1)return this._w=a,this._x=n,this._y=r,this._z=i,this;var s=1-o*o;if(s<=Number.EPSILON){var l=1-e;return this._w=l*a+e*this._w,this._x=l*n+e*this._x,this._y=l*r+e*this._y,this._z=l*i+e*this._z,this.normalize(),this._onChangeCallback(),this}var c=Math.sqrt(s),u=Math.atan2(c,o),h=Math.sin((1-e)*u)/c,d=Math.sin(e*u)/c;return this._w=a*h+this._w*d,this._x=n*h+this._x*d,this._y=r*h+this._y*d,this._z=i*h+this._z*d,this._onChangeCallback(),this},e.equals=function(t){return t._x===this._x&&t._y===this._y&&t._z===this._z&&t._w===this._w},e.fromArray=function(t,e){return void 0===e&&(e=0),this._x=t[e],this._y=t[e+1],this._z=t[e+2],this._w=t[e+3],this._onChangeCallback(),this},e.toArray=function(t,e){return void 0===t&&(t=[]),void 0===e&&(e=0),t[e]=this._x,t[e+1]=this._y,t[e+2]=this._z,t[e+3]=this._w,t},e.fromBufferAttribute=function(t,e){return this._x=t.getX(e),this._y=t.getY(e),this._z=t.getZ(e),this._w=t.getW(e),this},e._onChange=function(t){return this._onChangeCallback=t,this},e._onChangeCallback=function(){},st(t,[{key:"x",get:function(){return this._x},set:function(t){this._x=t,this._onChangeCallback()}},{key:"y",get:function(){return this._y},set:function(t){this._y=t,this._onChangeCallback()}},{key:"z",get:function(){return this._z},set:function(t){this._z=t,this._onChangeCallback()}},{key:"w",get:function(){return this._w},set:function(t){this._w=t,this._onChangeCallback()}}]),t}();Lt.prototype.isQuaternion=!0;var Rt=function(){function t(t,e,n){void 0===t&&(t=0),void 0===e&&(e=0),void 0===n&&(n=0),this.x=t,this.y=e,this.z=n}var e=t.prototype;return e.set=function(t,e,n){return void 0===n&&(n=this.z),this.x=t,this.y=e,this.z=n,this},e.setScalar=function(t){return this.x=t,this.y=t,this.z=t,this},e.setX=function(t){return this.x=t,this},e.setY=function(t){return this.y=t,this},e.setZ=function(t){return this.z=t,this},e.setComponent=function(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;default:throw new Error("index is out of range: "+t)}return this},e.getComponent=function(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+t)}},e.clone=function(){return new this.constructor(this.x,this.y,this.z)},e.copy=function(t){return this.x=t.x,this.y=t.y,this.z=t.z,this},e.add=function(t,e){return void 0!==e?(console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(t,e)):(this.x+=t.x,this.y+=t.y,this.z+=t.z,this)},e.addScalar=function(t){return this.x+=t,this.y+=t,this.z+=t,this},e.addVectors=function(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this},e.addScaledVector=function(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this},e.sub=function(t,e){return void 0!==e?(console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(t,e)):(this.x-=t.x,this.y-=t.y,this.z-=t.z,this)},e.subScalar=function(t){return this.x-=t,this.y-=t,this.z-=t,this},e.subVectors=function(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this},e.multiply=function(t,e){return void 0!==e?(console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(t,e)):(this.x*=t.x,this.y*=t.y,this.z*=t.z,this)},e.multiplyScalar=function(t){return this.x*=t,this.y*=t,this.z*=t,this},e.multiplyVectors=function(t,e){return this.x=t.x*e.x,this.y=t.y*e.y,this.z=t.z*e.z,this},e.applyEuler=function(t){return t&&t.isEuler||console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order."),this.applyQuaternion(Pt.setFromEuler(t))},e.applyAxisAngle=function(t,e){return this.applyQuaternion(Pt.setFromAxisAngle(t,e))},e.applyMatrix3=function(t){var e=this.x,n=this.y,r=this.z,i=t.elements;return this.x=i[0]*e+i[3]*n+i[6]*r,this.y=i[1]*e+i[4]*n+i[7]*r,this.z=i[2]*e+i[5]*n+i[8]*r,this},e.applyNormalMatrix=function(t){return this.applyMatrix3(t).normalize()},e.applyMatrix4=function(t){var e=this.x,n=this.y,r=this.z,i=t.elements,a=1/(i[3]*e+i[7]*n+i[11]*r+i[15]);return this.x=(i[0]*e+i[4]*n+i[8]*r+i[12])*a,this.y=(i[1]*e+i[5]*n+i[9]*r+i[13])*a,this.z=(i[2]*e+i[6]*n+i[10]*r+i[14])*a,this},e.applyQuaternion=function(t){var e=this.x,n=this.y,r=this.z,i=t.x,a=t.y,o=t.z,s=t.w,l=s*e+a*r-o*n,c=s*n+o*e-i*r,u=s*r+i*n-a*e,h=-i*e-a*n-o*r;return this.x=l*s+h*-i+c*-o-u*-a,this.y=c*s+h*-a+u*-i-l*-o,this.z=u*s+h*-o+l*-a-c*-i,this},e.project=function(t){return this.applyMatrix4(t.matrixWorldInverse).applyMatrix4(t.projectionMatrix)},e.unproject=function(t){return this.applyMatrix4(t.projectionMatrixInverse).applyMatrix4(t.matrixWorld)},e.transformDirection=function(t){var e=this.x,n=this.y,r=this.z,i=t.elements;return this.x=i[0]*e+i[4]*n+i[8]*r,this.y=i[1]*e+i[5]*n+i[9]*r,this.z=i[2]*e+i[6]*n+i[10]*r,this.normalize()},e.divide=function(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this},e.divideScalar=function(t){return this.multiplyScalar(1/t)},e.min=function(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this.z=Math.min(this.z,t.z),this},e.max=function(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this.z=Math.max(this.z,t.z),this},e.clamp=function(t,e){return this.x=Math.max(t.x,Math.min(e.x,this.x)),this.y=Math.max(t.y,Math.min(e.y,this.y)),this.z=Math.max(t.z,Math.min(e.z,this.z)),this},e.clampScalar=function(t,e){return this.x=Math.max(t,Math.min(e,this.x)),this.y=Math.max(t,Math.min(e,this.y)),this.z=Math.max(t,Math.min(e,this.z)),this},e.clampLength=function(t,e){var n=this.length();return this.divideScalar(n||1).multiplyScalar(Math.max(t,Math.min(e,n)))},e.floor=function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this},e.ceil=function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this},e.round=function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this},e.roundToZero=function(){return this.x=this.x<0?Math.ceil(this.x):Math.floor(this.x),this.y=this.y<0?Math.ceil(this.y):Math.floor(this.y),this.z=this.z<0?Math.ceil(this.z):Math.floor(this.z),this},e.negate=function(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this},e.dot=function(t){return this.x*t.x+this.y*t.y+this.z*t.z},e.lengthSq=function(){return this.x*this.x+this.y*this.y+this.z*this.z},e.length=function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},e.manhattanLength=function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)},e.normalize=function(){return this.divideScalar(this.length()||1)},e.setLength=function(t){return this.normalize().multiplyScalar(t)},e.lerp=function(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this.z+=(t.z-this.z)*e,this},e.lerpVectors=function(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this.z=t.z+(e.z-t.z)*n,this},e.cross=function(t,e){return void 0!==e?(console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(t,e)):this.crossVectors(this,t)},e.crossVectors=function(t,e){var n=t.x,r=t.y,i=t.z,a=e.x,o=e.y,s=e.z;return this.x=r*s-i*o,this.y=i*a-n*s,this.z=n*o-r*a,this},e.projectOnVector=function(t){var e=t.lengthSq();if(0===e)return this.set(0,0,0);var n=t.dot(this)/e;return this.copy(t).multiplyScalar(n)},e.projectOnPlane=function(t){return Ct.copy(this).projectOnVector(t),this.sub(Ct)},e.reflect=function(t){return this.sub(Ct.copy(t).multiplyScalar(2*this.dot(t)))},e.angleTo=function(t){var e=Math.sqrt(this.lengthSq()*t.lengthSq());if(0===e)return Math.PI/2;var n=this.dot(t)/e;return Math.acos(gt.clamp(n,-1,1))},e.distanceTo=function(t){return Math.sqrt(this.distanceToSquared(t))},e.distanceToSquared=function(t){var e=this.x-t.x,n=this.y-t.y,r=this.z-t.z;return e*e+n*n+r*r},e.manhattanDistanceTo=function(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)+Math.abs(this.z-t.z)},e.setFromSpherical=function(t){return this.setFromSphericalCoords(t.radius,t.phi,t.theta)},e.setFromSphericalCoords=function(t,e,n){var r=Math.sin(e)*t;return this.x=r*Math.sin(n),this.y=Math.cos(e)*t,this.z=r*Math.cos(n),this},e.setFromCylindrical=function(t){return this.setFromCylindricalCoords(t.radius,t.theta,t.y)},e.setFromCylindricalCoords=function(t,e,n){return this.x=t*Math.sin(e),this.y=n,this.z=t*Math.cos(e),this},e.setFromMatrixPosition=function(t){var e=t.elements;return this.x=e[12],this.y=e[13],this.z=e[14],this},e.setFromMatrixScale=function(t){var e=this.setFromMatrixColumn(t,0).length(),n=this.setFromMatrixColumn(t,1).length(),r=this.setFromMatrixColumn(t,2).length();return this.x=e,this.y=n,this.z=r,this},e.setFromMatrixColumn=function(t,e){return this.fromArray(t.elements,4*e)},e.setFromMatrix3Column=function(t,e){return this.fromArray(t.elements,3*e)},e.equals=function(t){return t.x===this.x&&t.y===this.y&&t.z===this.z},e.fromArray=function(t,e){return void 0===e&&(e=0),this.x=t[e],this.y=t[e+1],this.z=t[e+2],this},e.toArray=function(t,e){return void 0===t&&(t=[]),void 0===e&&(e=0),t[e]=this.x,t[e+1]=this.y,t[e+2]=this.z,t},e.fromBufferAttribute=function(t,e,n){return void 0!==n&&console.warn("THREE.Vector3: offset has been removed from .fromBufferAttribute()."),this.x=t.getX(e),this.y=t.getY(e),this.z=t.getZ(e),this},e.random=function(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this},t}();Rt.prototype.isVector3=!0;var Ct=new Rt,Pt=new Lt,Dt=function(){function t(t,e){void 0===t&&(t=new Rt(1/0,1/0,1/0)),void 0===e&&(e=new Rt(-1/0,-1/0,-1/0)),this.min=t,this.max=e}var e=t.prototype;return e.set=function(t,e){return this.min.copy(t),this.max.copy(e),this},e.setFromArray=function(t){for(var e=1/0,n=1/0,r=1/0,i=-1/0,a=-1/0,o=-1/0,s=0,l=t.length;si&&(i=c),u>a&&(a=u),h>o&&(o=h)}return this.min.set(e,n,r),this.max.set(i,a,o),this},e.setFromBufferAttribute=function(t){for(var e=1/0,n=1/0,r=1/0,i=-1/0,a=-1/0,o=-1/0,s=0,l=t.count;si&&(i=c),u>a&&(a=u),h>o&&(o=h)}return this.min.set(e,n,r),this.max.set(i,a,o),this},e.setFromPoints=function(t){this.makeEmpty();for(var e=0,n=t.length;ethis.max.x||t.ythis.max.y||t.zthis.max.z)},e.containsBox=function(t){return this.min.x<=t.min.x&&t.max.x<=this.max.x&&this.min.y<=t.min.y&&t.max.y<=this.max.y&&this.min.z<=t.min.z&&t.max.z<=this.max.z},e.getParameter=function(t,e){return void 0===e&&(console.warn("THREE.Box3: .getParameter() target is now required"),e=new Rt),e.set((t.x-this.min.x)/(this.max.x-this.min.x),(t.y-this.min.y)/(this.max.y-this.min.y),(t.z-this.min.z)/(this.max.z-this.min.z))},e.intersectsBox=function(t){return!(t.max.xthis.max.x||t.max.ythis.max.y||t.max.zthis.max.z)},e.intersectsSphere=function(t){return this.clampPoint(t.center,Nt),Nt.distanceToSquared(t.center)<=t.radius*t.radius},e.intersectsPlane=function(t){var e,n;return t.normal.x>0?(e=t.normal.x*this.min.x,n=t.normal.x*this.max.x):(e=t.normal.x*this.max.x,n=t.normal.x*this.min.x),t.normal.y>0?(e+=t.normal.y*this.min.y,n+=t.normal.y*this.max.y):(e+=t.normal.y*this.max.y,n+=t.normal.y*this.min.y),t.normal.z>0?(e+=t.normal.z*this.min.z,n+=t.normal.z*this.max.z):(e+=t.normal.z*this.max.z,n+=t.normal.z*this.min.z),e<=-t.constant&&n>=-t.constant},e.intersectsTriangle=function(t){if(this.isEmpty())return!1;this.getCenter(kt),Vt.subVectors(this.max,kt),Ot.subVectors(t.a,kt),zt.subVectors(t.b,kt),Ft.subVectors(t.c,kt),Ht.subVectors(zt,Ot),Gt.subVectors(Ft,zt),Ut.subVectors(Ot,Ft);var e=[0,-Ht.z,Ht.y,0,-Gt.z,Gt.y,0,-Ut.z,Ut.y,Ht.z,0,-Ht.x,Gt.z,0,-Gt.x,Ut.z,0,-Ut.x,-Ht.y,Ht.x,0,-Gt.y,Gt.x,0,-Ut.y,Ut.x,0];return!!qt(e,Ot,zt,Ft,Vt)&&(!!qt(e=[1,0,0,0,1,0,0,0,1],Ot,zt,Ft,Vt)&&(Wt.crossVectors(Ht,Gt),qt(e=[Wt.x,Wt.y,Wt.z],Ot,zt,Ft,Vt)))},e.clampPoint=function(t,e){return void 0===e&&(console.warn("THREE.Box3: .clampPoint() target is now required"),e=new Rt),e.copy(t).clamp(this.min,this.max)},e.distanceToPoint=function(t){return Nt.copy(t).clamp(this.min,this.max).sub(t).length()},e.getBoundingSphere=function(t){return void 0===t&&console.error("THREE.Box3: .getBoundingSphere() target is now required"),this.getCenter(t.center),t.radius=.5*this.getSize(Nt).length(),t},e.intersect=function(t){return this.min.max(t.min),this.max.min(t.max),this.isEmpty()&&this.makeEmpty(),this},e.union=function(t){return this.min.min(t.min),this.max.max(t.max),this},e.applyMatrix4=function(t){return this.isEmpty()||(It[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(t),It[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(t),It[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(t),It[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(t),It[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(t),It[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(t),It[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(t),It[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(t),this.setFromPoints(It)),this},e.translate=function(t){return this.min.add(t),this.max.add(t),this},e.equals=function(t){return t.min.equals(this.min)&&t.max.equals(this.max)},t}();Dt.prototype.isBox3=!0;var It=[new Rt,new Rt,new Rt,new Rt,new Rt,new Rt,new Rt,new Rt],Nt=new Rt,Bt=new Dt,Ot=new Rt,zt=new Rt,Ft=new Rt,Ht=new Rt,Gt=new Rt,Ut=new Rt,kt=new Rt,Vt=new Rt,Wt=new Rt,jt=new Rt;function qt(t,e,n,r,i){for(var a=0,o=t.length-3;a<=o;a+=3){jt.fromArray(t,a);var s=i.x*Math.abs(jt.x)+i.y*Math.abs(jt.y)+i.z*Math.abs(jt.z),l=e.dot(jt),c=n.dot(jt),u=r.dot(jt);if(Math.max(-Math.max(l,c,u),Math.min(l,c,u))>s)return!1}return!0}var Xt=new Dt,Yt=function(){function t(t,e){void 0===t&&(t=new Rt),void 0===e&&(e=-1),this.center=t,this.radius=e}var e=t.prototype;return e.set=function(t,e){return this.center.copy(t),this.radius=e,this},e.setFromPoints=function(t,e){var n=this.center;void 0!==e?n.copy(e):Xt.setFromPoints(t).getCenter(n);for(var r=0,i=0,a=t.length;ithis.radius*this.radius&&(e.sub(this.center).normalize(),e.multiplyScalar(this.radius).add(this.center)),e},e.getBoundingBox=function(t){return void 0===t&&(console.warn("THREE.Sphere: .getBoundingBox() target is now required"),t=new Dt),this.isEmpty()?(t.makeEmpty(),t):(t.set(this.center,this.center),t.expandByScalar(this.radius),t)},e.applyMatrix4=function(t){return this.center.applyMatrix4(t),this.radius=this.radius*t.getMaxScaleOnAxis(),this},e.translate=function(t){return this.center.add(t),this},e.equals=function(t){return t.center.equals(this.center)&&t.radius===this.radius},e.clone=function(){return(new this.constructor).copy(this)},t}(),Zt=new Rt,Jt=new Rt,Qt=new Rt,Kt=new Rt,$t=new Rt,te=new Rt,ee=new Rt,ne=function(){function t(t,e){void 0===t&&(t=new Rt),void 0===e&&(e=new Rt(0,0,-1)),this.origin=t,this.direction=e}var e=t.prototype;return e.set=function(t,e){return this.origin.copy(t),this.direction.copy(e),this},e.copy=function(t){return this.origin.copy(t.origin),this.direction.copy(t.direction),this},e.at=function(t,e){return void 0===e&&(console.warn("THREE.Ray: .at() target is now required"),e=new Rt),e.copy(this.direction).multiplyScalar(t).add(this.origin)},e.lookAt=function(t){return this.direction.copy(t).sub(this.origin).normalize(),this},e.recast=function(t){return this.origin.copy(this.at(t,Zt)),this},e.closestPointToPoint=function(t,e){void 0===e&&(console.warn("THREE.Ray: .closestPointToPoint() target is now required"),e=new Rt),e.subVectors(t,this.origin);var n=e.dot(this.direction);return n<0?e.copy(this.origin):e.copy(this.direction).multiplyScalar(n).add(this.origin)},e.distanceToPoint=function(t){return Math.sqrt(this.distanceSqToPoint(t))},e.distanceSqToPoint=function(t){var e=Zt.subVectors(t,this.origin).dot(this.direction);return e<0?this.origin.distanceToSquared(t):(Zt.copy(this.direction).multiplyScalar(e).add(this.origin),Zt.distanceToSquared(t))},e.distanceSqToSegment=function(t,e,n,r){Jt.copy(t).add(e).multiplyScalar(.5),Qt.copy(e).sub(t).normalize(),Kt.copy(this.origin).sub(Jt);var i,a,o,s,l=.5*t.distanceTo(e),c=-this.direction.dot(Qt),u=Kt.dot(this.direction),h=-Kt.dot(Qt),d=Kt.lengthSq(),p=Math.abs(1-c*c);if(p>0)if(a=c*u-h,s=l*p,(i=c*h-u)>=0)if(a>=-s)if(a<=s){var f=1/p;o=(i*=f)*(i+c*(a*=f)+2*u)+a*(c*i+a+2*h)+d}else a=l,o=-(i=Math.max(0,-(c*a+u)))*i+a*(a+2*h)+d;else a=-l,o=-(i=Math.max(0,-(c*a+u)))*i+a*(a+2*h)+d;else a<=-s?o=-(i=Math.max(0,-(-c*l+u)))*i+(a=i>0?-l:Math.min(Math.max(-l,-h),l))*(a+2*h)+d:a<=s?(i=0,o=(a=Math.min(Math.max(-l,-h),l))*(a+2*h)+d):o=-(i=Math.max(0,-(c*l+u)))*i+(a=i>0?l:Math.min(Math.max(-l,-h),l))*(a+2*h)+d;else a=c>0?-l:l,o=-(i=Math.max(0,-(c*a+u)))*i+a*(a+2*h)+d;return n&&n.copy(this.direction).multiplyScalar(i).add(this.origin),r&&r.copy(Qt).multiplyScalar(a).add(Jt),o},e.intersectSphere=function(t,e){Zt.subVectors(t.center,this.origin);var n=Zt.dot(this.direction),r=Zt.dot(Zt)-n*n,i=t.radius*t.radius;if(r>i)return null;var a=Math.sqrt(i-r),o=n-a,s=n+a;return o<0&&s<0?null:o<0?this.at(s,e):this.at(o,e)},e.intersectsSphere=function(t){return this.distanceSqToPoint(t.center)<=t.radius*t.radius},e.distanceToPlane=function(t){var e=t.normal.dot(this.direction);if(0===e)return 0===t.distanceToPoint(this.origin)?0:null;var n=-(this.origin.dot(t.normal)+t.constant)/e;return n>=0?n:null},e.intersectPlane=function(t,e){var n=this.distanceToPlane(t);return null===n?null:this.at(n,e)},e.intersectsPlane=function(t){var e=t.distanceToPoint(this.origin);return 0===e||t.normal.dot(this.direction)*e<0},e.intersectBox=function(t,e){var n,r,i,a,o,s,l=1/this.direction.x,c=1/this.direction.y,u=1/this.direction.z,h=this.origin;return l>=0?(n=(t.min.x-h.x)*l,r=(t.max.x-h.x)*l):(n=(t.max.x-h.x)*l,r=(t.min.x-h.x)*l),c>=0?(i=(t.min.y-h.y)*c,a=(t.max.y-h.y)*c):(i=(t.max.y-h.y)*c,a=(t.min.y-h.y)*c),n>a||i>r?null:((i>n||n!=n)&&(n=i),(a=0?(o=(t.min.z-h.z)*u,s=(t.max.z-h.z)*u):(o=(t.max.z-h.z)*u,s=(t.min.z-h.z)*u),n>s||o>r?null:((o>n||n!=n)&&(n=o),(s=0?n:r,e)))},e.intersectsBox=function(t){return null!==this.intersectBox(t,Zt)},e.intersectTriangle=function(t,e,n,r,i){$t.subVectors(e,t),te.subVectors(n,t),ee.crossVectors($t,te);var a,o=this.direction.dot(ee);if(o>0){if(r)return null;a=1}else{if(!(o<0))return null;a=-1,o=-o}Kt.subVectors(this.origin,t);var s=a*this.direction.dot(te.crossVectors(Kt,te));if(s<0)return null;var l=a*this.direction.dot($t.cross(Kt));if(l<0)return null;if(s+l>o)return null;var c=-a*Kt.dot(ee);return c<0?null:this.at(c/o,i)},e.applyMatrix4=function(t){return this.origin.applyMatrix4(t),this.direction.transformDirection(t),this},e.equals=function(t){return t.origin.equals(this.origin)&&t.direction.equals(this.direction)},e.clone=function(){return(new this.constructor).copy(this)},t}(),re=function(){function t(){this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],arguments.length>0&&console.error("THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.")}var e=t.prototype;return e.set=function(t,e,n,r,i,a,o,s,l,c,u,h,d,p,f,m){var v=this.elements;return v[0]=t,v[4]=e,v[8]=n,v[12]=r,v[1]=i,v[5]=a,v[9]=o,v[13]=s,v[2]=l,v[6]=c,v[10]=u,v[14]=h,v[3]=d,v[7]=p,v[11]=f,v[15]=m,this},e.identity=function(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this},e.clone=function(){return(new t).fromArray(this.elements)},e.copy=function(t){var e=this.elements,n=t.elements;return e[0]=n[0],e[1]=n[1],e[2]=n[2],e[3]=n[3],e[4]=n[4],e[5]=n[5],e[6]=n[6],e[7]=n[7],e[8]=n[8],e[9]=n[9],e[10]=n[10],e[11]=n[11],e[12]=n[12],e[13]=n[13],e[14]=n[14],e[15]=n[15],this},e.copyPosition=function(t){var e=this.elements,n=t.elements;return e[12]=n[12],e[13]=n[13],e[14]=n[14],this},e.setFromMatrix3=function(t){var e=t.elements;return this.set(e[0],e[3],e[6],0,e[1],e[4],e[7],0,e[2],e[5],e[8],0,0,0,0,1),this},e.extractBasis=function(t,e,n){return t.setFromMatrixColumn(this,0),e.setFromMatrixColumn(this,1),n.setFromMatrixColumn(this,2),this},e.makeBasis=function(t,e,n){return this.set(t.x,e.x,n.x,0,t.y,e.y,n.y,0,t.z,e.z,n.z,0,0,0,0,1),this},e.extractRotation=function(t){var e=this.elements,n=t.elements,r=1/ie.setFromMatrixColumn(t,0).length(),i=1/ie.setFromMatrixColumn(t,1).length(),a=1/ie.setFromMatrixColumn(t,2).length();return e[0]=n[0]*r,e[1]=n[1]*r,e[2]=n[2]*r,e[3]=0,e[4]=n[4]*i,e[5]=n[5]*i,e[6]=n[6]*i,e[7]=0,e[8]=n[8]*a,e[9]=n[9]*a,e[10]=n[10]*a,e[11]=0,e[12]=0,e[13]=0,e[14]=0,e[15]=1,this},e.makeRotationFromEuler=function(t){t&&t.isEuler||console.error("THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var e=this.elements,n=t.x,r=t.y,i=t.z,a=Math.cos(n),o=Math.sin(n),s=Math.cos(r),l=Math.sin(r),c=Math.cos(i),u=Math.sin(i);if("XYZ"===t.order){var h=a*c,d=a*u,p=o*c,f=o*u;e[0]=s*c,e[4]=-s*u,e[8]=l,e[1]=d+p*l,e[5]=h-f*l,e[9]=-o*s,e[2]=f-h*l,e[6]=p+d*l,e[10]=a*s}else if("YXZ"===t.order){var m=s*c,v=s*u,g=l*c,y=l*u;e[0]=m+y*o,e[4]=g*o-v,e[8]=a*l,e[1]=a*u,e[5]=a*c,e[9]=-o,e[2]=v*o-g,e[6]=y+m*o,e[10]=a*s}else if("ZXY"===t.order){var x=s*c,_=s*u,b=l*c,w=l*u;e[0]=x-w*o,e[4]=-a*u,e[8]=b+_*o,e[1]=_+b*o,e[5]=a*c,e[9]=w-x*o,e[2]=-a*l,e[6]=o,e[10]=a*s}else if("ZYX"===t.order){var M=a*c,S=a*u,T=o*c,E=o*u;e[0]=s*c,e[4]=T*l-S,e[8]=M*l+E,e[1]=s*u,e[5]=E*l+M,e[9]=S*l-T,e[2]=-l,e[6]=o*s,e[10]=a*s}else if("YZX"===t.order){var A=a*s,L=a*l,R=o*s,C=o*l;e[0]=s*c,e[4]=C-A*u,e[8]=R*u+L,e[1]=u,e[5]=a*c,e[9]=-o*c,e[2]=-l*c,e[6]=L*u+R,e[10]=A-C*u}else if("XZY"===t.order){var P=a*s,D=a*l,I=o*s,N=o*l;e[0]=s*c,e[4]=-u,e[8]=l*c,e[1]=P*u+N,e[5]=a*c,e[9]=D*u-I,e[2]=I*u-D,e[6]=o*c,e[10]=N*u+P}return e[3]=0,e[7]=0,e[11]=0,e[12]=0,e[13]=0,e[14]=0,e[15]=1,this},e.makeRotationFromQuaternion=function(t){return this.compose(oe,t,se)},e.lookAt=function(t,e,n){var r=this.elements;return ue.subVectors(t,e),0===ue.lengthSq()&&(ue.z=1),ue.normalize(),le.crossVectors(n,ue),0===le.lengthSq()&&(1===Math.abs(n.z)?ue.x+=1e-4:ue.z+=1e-4,ue.normalize(),le.crossVectors(n,ue)),le.normalize(),ce.crossVectors(ue,le),r[0]=le.x,r[4]=ce.x,r[8]=ue.x,r[1]=le.y,r[5]=ce.y,r[9]=ue.y,r[2]=le.z,r[6]=ce.z,r[10]=ue.z,this},e.multiply=function(t,e){return void 0!==e?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(t,e)):this.multiplyMatrices(this,t)},e.premultiply=function(t){return this.multiplyMatrices(t,this)},e.multiplyMatrices=function(t,e){var n=t.elements,r=e.elements,i=this.elements,a=n[0],o=n[4],s=n[8],l=n[12],c=n[1],u=n[5],h=n[9],d=n[13],p=n[2],f=n[6],m=n[10],v=n[14],g=n[3],y=n[7],x=n[11],_=n[15],b=r[0],w=r[4],M=r[8],S=r[12],T=r[1],E=r[5],A=r[9],L=r[13],R=r[2],C=r[6],P=r[10],D=r[14],I=r[3],N=r[7],B=r[11],O=r[15];return i[0]=a*b+o*T+s*R+l*I,i[4]=a*w+o*E+s*C+l*N,i[8]=a*M+o*A+s*P+l*B,i[12]=a*S+o*L+s*D+l*O,i[1]=c*b+u*T+h*R+d*I,i[5]=c*w+u*E+h*C+d*N,i[9]=c*M+u*A+h*P+d*B,i[13]=c*S+u*L+h*D+d*O,i[2]=p*b+f*T+m*R+v*I,i[6]=p*w+f*E+m*C+v*N,i[10]=p*M+f*A+m*P+v*B,i[14]=p*S+f*L+m*D+v*O,i[3]=g*b+y*T+x*R+_*I,i[7]=g*w+y*E+x*C+_*N,i[11]=g*M+y*A+x*P+_*B,i[15]=g*S+y*L+x*D+_*O,this},e.multiplyScalar=function(t){var e=this.elements;return e[0]*=t,e[4]*=t,e[8]*=t,e[12]*=t,e[1]*=t,e[5]*=t,e[9]*=t,e[13]*=t,e[2]*=t,e[6]*=t,e[10]*=t,e[14]*=t,e[3]*=t,e[7]*=t,e[11]*=t,e[15]*=t,this},e.determinant=function(){var t=this.elements,e=t[0],n=t[4],r=t[8],i=t[12],a=t[1],o=t[5],s=t[9],l=t[13],c=t[2],u=t[6],h=t[10],d=t[14];return t[3]*(+i*s*u-r*l*u-i*o*h+n*l*h+r*o*d-n*s*d)+t[7]*(+e*s*d-e*l*h+i*a*h-r*a*d+r*l*c-i*s*c)+t[11]*(+e*l*u-e*o*d-i*a*u+n*a*d+i*o*c-n*l*c)+t[15]*(-r*o*c-e*s*u+e*o*h+r*a*u-n*a*h+n*s*c)},e.transpose=function(){var t,e=this.elements;return t=e[1],e[1]=e[4],e[4]=t,t=e[2],e[2]=e[8],e[8]=t,t=e[6],e[6]=e[9],e[9]=t,t=e[3],e[3]=e[12],e[12]=t,t=e[7],e[7]=e[13],e[13]=t,t=e[11],e[11]=e[14],e[14]=t,this},e.setPosition=function(t,e,n){var r=this.elements;return t.isVector3?(r[12]=t.x,r[13]=t.y,r[14]=t.z):(r[12]=t,r[13]=e,r[14]=n),this},e.invert=function(){var t=this.elements,e=t[0],n=t[1],r=t[2],i=t[3],a=t[4],o=t[5],s=t[6],l=t[7],c=t[8],u=t[9],h=t[10],d=t[11],p=t[12],f=t[13],m=t[14],v=t[15],g=u*m*l-f*h*l+f*s*d-o*m*d-u*s*v+o*h*v,y=p*h*l-c*m*l-p*s*d+a*m*d+c*s*v-a*h*v,x=c*f*l-p*u*l+p*o*d-a*f*d-c*o*v+a*u*v,_=p*u*s-c*f*s-p*o*h+a*f*h+c*o*m-a*u*m,b=e*g+n*y+r*x+i*_;if(0===b)return this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);var w=1/b;return t[0]=g*w,t[1]=(f*h*i-u*m*i-f*r*d+n*m*d+u*r*v-n*h*v)*w,t[2]=(o*m*i-f*s*i+f*r*l-n*m*l-o*r*v+n*s*v)*w,t[3]=(u*s*i-o*h*i-u*r*l+n*h*l+o*r*d-n*s*d)*w,t[4]=y*w,t[5]=(c*m*i-p*h*i+p*r*d-e*m*d-c*r*v+e*h*v)*w,t[6]=(p*s*i-a*m*i-p*r*l+e*m*l+a*r*v-e*s*v)*w,t[7]=(a*h*i-c*s*i+c*r*l-e*h*l-a*r*d+e*s*d)*w,t[8]=x*w,t[9]=(p*u*i-c*f*i-p*n*d+e*f*d+c*n*v-e*u*v)*w,t[10]=(a*f*i-p*o*i+p*n*l-e*f*l-a*n*v+e*o*v)*w,t[11]=(c*o*i-a*u*i-c*n*l+e*u*l+a*n*d-e*o*d)*w,t[12]=_*w,t[13]=(c*f*r-p*u*r+p*n*h-e*f*h-c*n*m+e*u*m)*w,t[14]=(p*o*r-a*f*r-p*n*s+e*f*s+a*n*m-e*o*m)*w,t[15]=(a*u*r-c*o*r+c*n*s-e*u*s-a*n*h+e*o*h)*w,this},e.scale=function(t){var e=this.elements,n=t.x,r=t.y,i=t.z;return e[0]*=n,e[4]*=r,e[8]*=i,e[1]*=n,e[5]*=r,e[9]*=i,e[2]*=n,e[6]*=r,e[10]*=i,e[3]*=n,e[7]*=r,e[11]*=i,this},e.getMaxScaleOnAxis=function(){var t=this.elements,e=t[0]*t[0]+t[1]*t[1]+t[2]*t[2],n=t[4]*t[4]+t[5]*t[5]+t[6]*t[6],r=t[8]*t[8]+t[9]*t[9]+t[10]*t[10];return Math.sqrt(Math.max(e,n,r))},e.makeTranslation=function(t,e,n){return this.set(1,0,0,t,0,1,0,e,0,0,1,n,0,0,0,1),this},e.makeRotationX=function(t){var e=Math.cos(t),n=Math.sin(t);return this.set(1,0,0,0,0,e,-n,0,0,n,e,0,0,0,0,1),this},e.makeRotationY=function(t){var e=Math.cos(t),n=Math.sin(t);return this.set(e,0,n,0,0,1,0,0,-n,0,e,0,0,0,0,1),this},e.makeRotationZ=function(t){var e=Math.cos(t),n=Math.sin(t);return this.set(e,-n,0,0,n,e,0,0,0,0,1,0,0,0,0,1),this},e.makeRotationAxis=function(t,e){var n=Math.cos(e),r=Math.sin(e),i=1-n,a=t.x,o=t.y,s=t.z,l=i*a,c=i*o;return this.set(l*a+n,l*o-r*s,l*s+r*o,0,l*o+r*s,c*o+n,c*s-r*a,0,l*s-r*o,c*s+r*a,i*s*s+n,0,0,0,0,1),this},e.makeScale=function(t,e,n){return this.set(t,0,0,0,0,e,0,0,0,0,n,0,0,0,0,1),this},e.makeShear=function(t,e,n){return this.set(1,e,n,0,t,1,n,0,t,e,1,0,0,0,0,1),this},e.compose=function(t,e,n){var r=this.elements,i=e._x,a=e._y,o=e._z,s=e._w,l=i+i,c=a+a,u=o+o,h=i*l,d=i*c,p=i*u,f=a*c,m=a*u,v=o*u,g=s*l,y=s*c,x=s*u,_=n.x,b=n.y,w=n.z;return r[0]=(1-(f+v))*_,r[1]=(d+x)*_,r[2]=(p-y)*_,r[3]=0,r[4]=(d-x)*b,r[5]=(1-(h+v))*b,r[6]=(m+g)*b,r[7]=0,r[8]=(p+y)*w,r[9]=(m-g)*w,r[10]=(1-(h+f))*w,r[11]=0,r[12]=t.x,r[13]=t.y,r[14]=t.z,r[15]=1,this},e.decompose=function(t,e,n){var r=this.elements,i=ie.set(r[0],r[1],r[2]).length(),a=ie.set(r[4],r[5],r[6]).length(),o=ie.set(r[8],r[9],r[10]).length();this.determinant()<0&&(i=-i),t.x=r[12],t.y=r[13],t.z=r[14],ae.copy(this);var s=1/i,l=1/a,c=1/o;return ae.elements[0]*=s,ae.elements[1]*=s,ae.elements[2]*=s,ae.elements[4]*=l,ae.elements[5]*=l,ae.elements[6]*=l,ae.elements[8]*=c,ae.elements[9]*=c,ae.elements[10]*=c,e.setFromRotationMatrix(ae),n.x=i,n.y=a,n.z=o,this},e.makePerspective=function(t,e,n,r,i,a){void 0===a&&console.warn("THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.");var o=this.elements,s=2*i/(e-t),l=2*i/(n-r),c=(e+t)/(e-t),u=(n+r)/(n-r),h=-(a+i)/(a-i),d=-2*a*i/(a-i);return o[0]=s,o[4]=0,o[8]=c,o[12]=0,o[1]=0,o[5]=l,o[9]=u,o[13]=0,o[2]=0,o[6]=0,o[10]=h,o[14]=d,o[3]=0,o[7]=0,o[11]=-1,o[15]=0,this},e.makeOrthographic=function(t,e,n,r,i,a){var o=this.elements,s=1/(e-t),l=1/(n-r),c=1/(a-i),u=(e+t)*s,h=(n+r)*l,d=(a+i)*c;return o[0]=2*s,o[4]=0,o[8]=0,o[12]=-u,o[1]=0,o[5]=2*l,o[9]=0,o[13]=-h,o[2]=0,o[6]=0,o[10]=-2*c,o[14]=-d,o[3]=0,o[7]=0,o[11]=0,o[15]=1,this},e.equals=function(t){for(var e=this.elements,n=t.elements,r=0;r<16;r++)if(e[r]!==n[r])return!1;return!0},e.fromArray=function(t,e){void 0===e&&(e=0);for(var n=0;n<16;n++)this.elements[n]=t[n+e];return this},e.toArray=function(t,e){void 0===t&&(t=[]),void 0===e&&(e=0);var n=this.elements;return t[e]=n[0],t[e+1]=n[1],t[e+2]=n[2],t[e+3]=n[3],t[e+4]=n[4],t[e+5]=n[5],t[e+6]=n[6],t[e+7]=n[7],t[e+8]=n[8],t[e+9]=n[9],t[e+10]=n[10],t[e+11]=n[11],t[e+12]=n[12],t[e+13]=n[13],t[e+14]=n[14],t[e+15]=n[15],t},t}();re.prototype.isMatrix4=!0;var ie=new Rt,ae=new re,oe=new Rt(0,0,0),se=new Rt(1,1,1),le=new Rt,ce=new Rt,ue=new Rt,he=new re,de=new Lt,pe=function(){function t(e,n,r,i){void 0===e&&(e=0),void 0===n&&(n=0),void 0===r&&(r=0),void 0===i&&(i=t.DefaultOrder),this._x=e,this._y=n,this._z=r,this._order=i}var e=t.prototype;return e.set=function(t,e,n,r){return this._x=t,this._y=e,this._z=n,this._order=r||this._order,this._onChangeCallback(),this},e.clone=function(){return new this.constructor(this._x,this._y,this._z,this._order)},e.copy=function(t){return this._x=t._x,this._y=t._y,this._z=t._z,this._order=t._order,this._onChangeCallback(),this},e.setFromRotationMatrix=function(t,e,n){var r=gt.clamp,i=t.elements,a=i[0],o=i[4],s=i[8],l=i[1],c=i[5],u=i[9],h=i[2],d=i[6],p=i[10];switch(e=e||this._order){case"XYZ":this._y=Math.asin(r(s,-1,1)),Math.abs(s)<.9999999?(this._x=Math.atan2(-u,p),this._z=Math.atan2(-o,a)):(this._x=Math.atan2(d,c),this._z=0);break;case"YXZ":this._x=Math.asin(-r(u,-1,1)),Math.abs(u)<.9999999?(this._y=Math.atan2(s,p),this._z=Math.atan2(l,c)):(this._y=Math.atan2(-h,a),this._z=0);break;case"ZXY":this._x=Math.asin(r(d,-1,1)),Math.abs(d)<.9999999?(this._y=Math.atan2(-h,p),this._z=Math.atan2(-o,c)):(this._y=0,this._z=Math.atan2(l,a));break;case"ZYX":this._y=Math.asin(-r(h,-1,1)),Math.abs(h)<.9999999?(this._x=Math.atan2(d,p),this._z=Math.atan2(l,a)):(this._x=0,this._z=Math.atan2(-o,c));break;case"YZX":this._z=Math.asin(r(l,-1,1)),Math.abs(l)<.9999999?(this._x=Math.atan2(-u,c),this._y=Math.atan2(-h,a)):(this._x=0,this._y=Math.atan2(s,p));break;case"XZY":this._z=Math.asin(-r(o,-1,1)),Math.abs(o)<.9999999?(this._x=Math.atan2(d,c),this._y=Math.atan2(s,a)):(this._x=Math.atan2(-u,p),this._y=0);break;default:console.warn("THREE.Euler: .setFromRotationMatrix() encountered an unknown order: "+e)}return this._order=e,!1!==n&&this._onChangeCallback(),this},e.setFromQuaternion=function(t,e,n){return he.makeRotationFromQuaternion(t),this.setFromRotationMatrix(he,e,n)},e.setFromVector3=function(t,e){return this.set(t.x,t.y,t.z,e||this._order)},e.reorder=function(t){return de.setFromEuler(this),this.setFromQuaternion(de,t)},e.equals=function(t){return t._x===this._x&&t._y===this._y&&t._z===this._z&&t._order===this._order},e.fromArray=function(t){return this._x=t[0],this._y=t[1],this._z=t[2],void 0!==t[3]&&(this._order=t[3]),this._onChangeCallback(),this},e.toArray=function(t,e){return void 0===t&&(t=[]),void 0===e&&(e=0),t[e]=this._x,t[e+1]=this._y,t[e+2]=this._z,t[e+3]=this._order,t},e.toVector3=function(t){return t?t.set(this._x,this._y,this._z):new Rt(this._x,this._y,this._z)},e._onChange=function(t){return this._onChangeCallback=t,this},e._onChangeCallback=function(){},st(t,[{key:"x",get:function(){return this._x},set:function(t){this._x=t,this._onChangeCallback()}},{key:"y",get:function(){return this._y},set:function(t){this._y=t,this._onChangeCallback()}},{key:"z",get:function(){return this._z},set:function(t){this._z=t,this._onChangeCallback()}},{key:"order",get:function(){return this._order},set:function(t){this._order=t,this._onChangeCallback()}}]),t}();pe.prototype.isEuler=!0,pe.DefaultOrder="XYZ",pe.RotationOrders=["XYZ","YZX","ZXY","XZY","YXZ","ZYX"];var fe=function(){function t(){this.mask=1}var e=t.prototype;return e.set=function(t){this.mask=1<1){for(var e=0;e1){for(var e=0;e0){r.children=[];for(var p=0;p0){r.animations=[];for(var f=0;f0&&(n.geometries=v),g.length>0&&(n.materials=g),y.length>0&&(n.textures=y),x.length>0&&(n.images=x),_.length>0&&(n.shapes=_),b.length>0&&(n.skeletons=b),w.length>0&&(n.animations=w)}return n.object=r,n;function M(t){var e=[];for(var n in t){var r=t[n];delete r.metadata,e.push(r)}return e}},clone:function(t){return(new this.constructor).copy(this,t)},copy:function(t,e){if(void 0===e&&(e=!0),this.name=t.name,this.up.copy(t.up),this.position.copy(t.position),this.rotation.order=t.rotation.order,this.quaternion.copy(t.quaternion),this.scale.copy(t.scale),this.matrix.copy(t.matrix),this.matrixWorld.copy(t.matrixWorld),this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrixWorldNeedsUpdate=t.matrixWorldNeedsUpdate,this.layers.mask=t.layers.mask,this.visible=t.visible,this.castShadow=t.castShadow,this.receiveShadow=t.receiveShadow,this.frustumCulled=t.frustumCulled,this.renderOrder=t.renderOrder,this.userData=JSON.parse(JSON.stringify(t.userData)),!0===e)for(var n=0;n1?void 0:e.copy(n).multiplyScalar(i).add(t.start)},e.intersectsLine=function(t){var e=this.distanceToPoint(t.start),n=this.distanceToPoint(t.end);return e<0&&n>0||n<0&&e>0},e.intersectsBox=function(t){return t.intersectsPlane(this)},e.intersectsSphere=function(t){return t.intersectsPlane(this)},e.coplanarPoint=function(t){return void 0===t&&(console.warn("THREE.Plane: .coplanarPoint() target is now required"),t=new Rt),t.copy(this.normal).multiplyScalar(-this.constant)},e.applyMatrix4=function(t,e){var n=e||Pe.getNormalMatrix(t),r=this.coplanarPoint(Re).applyMatrix4(t),i=this.normal.applyMatrix3(n).normalize();return this.constant=-r.dot(i),this},e.translate=function(t){return this.constant-=t.dot(this.normal),this},e.equals=function(t){return t.normal.equals(this.normal)&&t.constant===this.constant},e.clone=function(){return(new this.constructor).copy(this)},t}();De.prototype.isPlane=!0;var Ie=new Rt,Ne=new Rt,Be=new Rt,Oe=new Rt,ze=new Rt,Fe=new Rt,He=new Rt,Ge=new Rt,Ue=new Rt,ke=new Rt,Ve=function(){function t(t,e,n){void 0===t&&(t=new Rt),void 0===e&&(e=new Rt),void 0===n&&(n=new Rt),this.a=t,this.b=e,this.c=n}t.getNormal=function(t,e,n,r){void 0===r&&(console.warn("THREE.Triangle: .getNormal() target is now required"),r=new Rt),r.subVectors(n,e),Ie.subVectors(t,e),r.cross(Ie);var i=r.lengthSq();return i>0?r.multiplyScalar(1/Math.sqrt(i)):r.set(0,0,0)},t.getBarycoord=function(t,e,n,r,i){Ie.subVectors(r,e),Ne.subVectors(n,e),Be.subVectors(t,e);var a=Ie.dot(Ie),o=Ie.dot(Ne),s=Ie.dot(Be),l=Ne.dot(Ne),c=Ne.dot(Be),u=a*l-o*o;if(void 0===i&&(console.warn("THREE.Triangle: .getBarycoord() target is now required"),i=new Rt),0===u)return i.set(-2,-1,-1);var h=1/u,d=(l*s-o*c)*h,p=(a*c-o*s)*h;return i.set(1-d-p,p,d)},t.containsPoint=function(t,e,n,r){return this.getBarycoord(t,e,n,r,Oe),Oe.x>=0&&Oe.y>=0&&Oe.x+Oe.y<=1},t.getUV=function(t,e,n,r,i,a,o,s){return this.getBarycoord(t,e,n,r,Oe),s.set(0,0),s.addScaledVector(i,Oe.x),s.addScaledVector(a,Oe.y),s.addScaledVector(o,Oe.z),s},t.isFrontFacing=function(t,e,n,r){return Ie.subVectors(n,e),Ne.subVectors(t,e),Ie.cross(Ne).dot(r)<0};var e=t.prototype;return e.set=function(t,e,n){return this.a.copy(t),this.b.copy(e),this.c.copy(n),this},e.setFromPointsAndIndices=function(t,e,n,r){return this.a.copy(t[e]),this.b.copy(t[n]),this.c.copy(t[r]),this},e.clone=function(){return(new this.constructor).copy(this)},e.copy=function(t){return this.a.copy(t.a),this.b.copy(t.b),this.c.copy(t.c),this},e.getArea=function(){return Ie.subVectors(this.c,this.b),Ne.subVectors(this.a,this.b),.5*Ie.cross(Ne).length()},e.getMidpoint=function(t){return void 0===t&&(console.warn("THREE.Triangle: .getMidpoint() target is now required"),t=new Rt),t.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},e.getNormal=function(e){return t.getNormal(this.a,this.b,this.c,e)},e.getPlane=function(t){return void 0===t&&(console.warn("THREE.Triangle: .getPlane() target is now required"),t=new De),t.setFromCoplanarPoints(this.a,this.b,this.c)},e.getBarycoord=function(e,n){return t.getBarycoord(e,this.a,this.b,this.c,n)},e.getUV=function(e,n,r,i,a){return t.getUV(e,this.a,this.b,this.c,n,r,i,a)},e.containsPoint=function(e){return t.containsPoint(e,this.a,this.b,this.c)},e.isFrontFacing=function(e){return t.isFrontFacing(this.a,this.b,this.c,e)},e.intersectsBox=function(t){return t.intersectsTriangle(this)},e.closestPointToPoint=function(t,e){void 0===e&&(console.warn("THREE.Triangle: .closestPointToPoint() target is now required"),e=new Rt);var n,r,i=this.a,a=this.b,o=this.c;ze.subVectors(a,i),Fe.subVectors(o,i),Ge.subVectors(t,i);var s=ze.dot(Ge),l=Fe.dot(Ge);if(s<=0&&l<=0)return e.copy(i);Ue.subVectors(t,a);var c=ze.dot(Ue),u=Fe.dot(Ue);if(c>=0&&u<=c)return e.copy(a);var h=s*u-c*l;if(h<=0&&s>=0&&c<=0)return n=s/(s-c),e.copy(i).addScaledVector(ze,n);ke.subVectors(t,o);var d=ze.dot(ke),p=Fe.dot(ke);if(p>=0&&d<=p)return e.copy(o);var f=d*l-s*p;if(f<=0&&l>=0&&p<=0)return r=l/(l-p),e.copy(i).addScaledVector(Fe,r);var m=c*p-d*u;if(m<=0&&u-c>=0&&d-p>=0)return He.subVectors(o,a),r=(u-c)/(u-c+(d-p)),e.copy(a).addScaledVector(He,r);var v=1/(m+f+h);return n=f*v,r=h*v,e.copy(i).addScaledVector(ze,n).addScaledVector(Fe,r)},e.equals=function(t){return t.a.equals(this.a)&&t.b.equals(this.b)&&t.c.equals(this.c)},t}(),We=0;function je(){Object.defineProperty(this,"id",{value:We++}),this.uuid=gt.generateUUID(),this.name="",this.type="Material",this.fog=!0,this.blending=1,this.side=0,this.vertexColors=!1,this.opacity=1,this.transparent=!1,this.blendSrc=204,this.blendDst=205,this.blendEquation=r,this.blendSrcAlpha=null,this.blendDstAlpha=null,this.blendEquationAlpha=null,this.depthFunc=3,this.depthTest=!0,this.depthWrite=!0,this.stencilWriteMask=255,this.stencilFunc=519,this.stencilRef=0,this.stencilFuncMask=255,this.stencilFail=et,this.stencilZFail=et,this.stencilZPass=et,this.stencilWrite=!1,this.clippingPlanes=null,this.clipIntersection=!1,this.clipShadows=!1,this.shadowSide=null,this.colorWrite=!0,this.precision=null,this.polygonOffset=!1,this.polygonOffsetFactor=0,this.polygonOffsetUnits=0,this.dithering=!1,this.alphaTest=0,this.premultipliedAlpha=!1,this.visible=!0,this.toneMapped=!0,this.userData={},this.version=0}je.prototype=Object.assign(Object.create(pt.prototype),{constructor:je,isMaterial:!0,onBeforeCompile:function(){},customProgramCacheKey:function(){return this.onBeforeCompile.toString()},setValues:function(t){if(void 0!==t)for(var e in t){var n=t[e];if(void 0!==n)if("shading"!==e){var r=this[e];void 0!==r?r&&r.isColor?r.set(n):r&&r.isVector3&&n&&n.isVector3?r.copy(n):this[e]=n:console.warn("THREE."+this.type+": '"+e+"' is not a property of this material.")}else console.warn("THREE."+this.type+": .shading has been removed. Use the boolean .flatShading instead."),this.flatShading=1===n;else console.warn("THREE.Material: '"+e+"' parameter is undefined.")}},toJSON:function(t){var e=void 0===t||"string"==typeof t;e&&(t={textures:{},images:{}});var n={metadata:{version:4.5,type:"Material",generator:"Material.toJSON"}};function r(t){var e=[];for(var n in t){var r=t[n];delete r.metadata,e.push(r)}return e}if(n.uuid=this.uuid,n.type=this.type,""!==this.name&&(n.name=this.name),this.color&&this.color.isColor&&(n.color=this.color.getHex()),void 0!==this.roughness&&(n.roughness=this.roughness),void 0!==this.metalness&&(n.metalness=this.metalness),this.sheen&&this.sheen.isColor&&(n.sheen=this.sheen.getHex()),this.emissive&&this.emissive.isColor&&(n.emissive=this.emissive.getHex()),this.emissiveIntensity&&1!==this.emissiveIntensity&&(n.emissiveIntensity=this.emissiveIntensity),this.specular&&this.specular.isColor&&(n.specular=this.specular.getHex()),void 0!==this.shininess&&(n.shininess=this.shininess),void 0!==this.clearcoat&&(n.clearcoat=this.clearcoat),void 0!==this.clearcoatRoughness&&(n.clearcoatRoughness=this.clearcoatRoughness),this.clearcoatMap&&this.clearcoatMap.isTexture&&(n.clearcoatMap=this.clearcoatMap.toJSON(t).uuid),this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture&&(n.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(t).uuid),this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture&&(n.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(t).uuid,n.clearcoatNormalScale=this.clearcoatNormalScale.toArray()),this.map&&this.map.isTexture&&(n.map=this.map.toJSON(t).uuid),this.matcap&&this.matcap.isTexture&&(n.matcap=this.matcap.toJSON(t).uuid),this.alphaMap&&this.alphaMap.isTexture&&(n.alphaMap=this.alphaMap.toJSON(t).uuid),this.lightMap&&this.lightMap.isTexture&&(n.lightMap=this.lightMap.toJSON(t).uuid,n.lightMapIntensity=this.lightMapIntensity),this.aoMap&&this.aoMap.isTexture&&(n.aoMap=this.aoMap.toJSON(t).uuid,n.aoMapIntensity=this.aoMapIntensity),this.bumpMap&&this.bumpMap.isTexture&&(n.bumpMap=this.bumpMap.toJSON(t).uuid,n.bumpScale=this.bumpScale),this.normalMap&&this.normalMap.isTexture&&(n.normalMap=this.normalMap.toJSON(t).uuid,n.normalMapType=this.normalMapType,n.normalScale=this.normalScale.toArray()),this.displacementMap&&this.displacementMap.isTexture&&(n.displacementMap=this.displacementMap.toJSON(t).uuid,n.displacementScale=this.displacementScale,n.displacementBias=this.displacementBias),this.roughnessMap&&this.roughnessMap.isTexture&&(n.roughnessMap=this.roughnessMap.toJSON(t).uuid),this.metalnessMap&&this.metalnessMap.isTexture&&(n.metalnessMap=this.metalnessMap.toJSON(t).uuid),this.emissiveMap&&this.emissiveMap.isTexture&&(n.emissiveMap=this.emissiveMap.toJSON(t).uuid),this.specularMap&&this.specularMap.isTexture&&(n.specularMap=this.specularMap.toJSON(t).uuid),this.envMap&&this.envMap.isTexture&&(n.envMap=this.envMap.toJSON(t).uuid,n.reflectivity=this.reflectivity,n.refractionRatio=this.refractionRatio,void 0!==this.combine&&(n.combine=this.combine),void 0!==this.envMapIntensity&&(n.envMapIntensity=this.envMapIntensity)),this.gradientMap&&this.gradientMap.isTexture&&(n.gradientMap=this.gradientMap.toJSON(t).uuid),void 0!==this.size&&(n.size=this.size),void 0!==this.sizeAttenuation&&(n.sizeAttenuation=this.sizeAttenuation),1!==this.blending&&(n.blending=this.blending),0!==this.side&&(n.side=this.side),this.vertexColors&&(n.vertexColors=!0),this.opacity<1&&(n.opacity=this.opacity),!0===this.transparent&&(n.transparent=this.transparent),n.depthFunc=this.depthFunc,n.depthTest=this.depthTest,n.depthWrite=this.depthWrite,n.stencilWrite=this.stencilWrite,n.stencilWriteMask=this.stencilWriteMask,n.stencilFunc=this.stencilFunc,n.stencilRef=this.stencilRef,n.stencilFuncMask=this.stencilFuncMask,n.stencilFail=this.stencilFail,n.stencilZFail=this.stencilZFail,n.stencilZPass=this.stencilZPass,this.rotation&&0!==this.rotation&&(n.rotation=this.rotation),!0===this.polygonOffset&&(n.polygonOffset=!0),0!==this.polygonOffsetFactor&&(n.polygonOffsetFactor=this.polygonOffsetFactor),0!==this.polygonOffsetUnits&&(n.polygonOffsetUnits=this.polygonOffsetUnits),this.linewidth&&1!==this.linewidth&&(n.linewidth=this.linewidth),void 0!==this.dashSize&&(n.dashSize=this.dashSize),void 0!==this.gapSize&&(n.gapSize=this.gapSize),void 0!==this.scale&&(n.scale=this.scale),!0===this.dithering&&(n.dithering=!0),this.alphaTest>0&&(n.alphaTest=this.alphaTest),!0===this.premultipliedAlpha&&(n.premultipliedAlpha=this.premultipliedAlpha),!0===this.wireframe&&(n.wireframe=this.wireframe),this.wireframeLinewidth>1&&(n.wireframeLinewidth=this.wireframeLinewidth),"round"!==this.wireframeLinecap&&(n.wireframeLinecap=this.wireframeLinecap),"round"!==this.wireframeLinejoin&&(n.wireframeLinejoin=this.wireframeLinejoin),!0===this.morphTargets&&(n.morphTargets=!0),!0===this.morphNormals&&(n.morphNormals=!0),!0===this.skinning&&(n.skinning=!0),!0===this.flatShading&&(n.flatShading=this.flatShading),!1===this.visible&&(n.visible=!1),!1===this.toneMapped&&(n.toneMapped=!1),"{}"!==JSON.stringify(this.userData)&&(n.userData=this.userData),e){var i=r(t.textures),a=r(t.images);i.length>0&&(n.textures=i),a.length>0&&(n.images=a)}return n},clone:function(){return(new this.constructor).copy(this)},copy:function(t){this.name=t.name,this.fog=t.fog,this.blending=t.blending,this.side=t.side,this.vertexColors=t.vertexColors,this.opacity=t.opacity,this.transparent=t.transparent,this.blendSrc=t.blendSrc,this.blendDst=t.blendDst,this.blendEquation=t.blendEquation,this.blendSrcAlpha=t.blendSrcAlpha,this.blendDstAlpha=t.blendDstAlpha,this.blendEquationAlpha=t.blendEquationAlpha,this.depthFunc=t.depthFunc,this.depthTest=t.depthTest,this.depthWrite=t.depthWrite,this.stencilWriteMask=t.stencilWriteMask,this.stencilFunc=t.stencilFunc,this.stencilRef=t.stencilRef,this.stencilFuncMask=t.stencilFuncMask,this.stencilFail=t.stencilFail,this.stencilZFail=t.stencilZFail,this.stencilZPass=t.stencilZPass,this.stencilWrite=t.stencilWrite;var e=t.clippingPlanes,n=null;if(null!==e){var r=e.length;n=new Array(r);for(var i=0;i!==r;++i)n[i]=e[i].clone()}return this.clippingPlanes=n,this.clipIntersection=t.clipIntersection,this.clipShadows=t.clipShadows,this.shadowSide=t.shadowSide,this.colorWrite=t.colorWrite,this.precision=t.precision,this.polygonOffset=t.polygonOffset,this.polygonOffsetFactor=t.polygonOffsetFactor,this.polygonOffsetUnits=t.polygonOffsetUnits,this.dithering=t.dithering,this.alphaTest=t.alphaTest,this.premultipliedAlpha=t.premultipliedAlpha,this.visible=t.visible,this.toneMapped=t.toneMapped,this.userData=JSON.parse(JSON.stringify(t.userData)),this},dispose:function(){this.dispatchEvent({type:"dispose"})}}),Object.defineProperty(je.prototype,"needsUpdate",{set:function(t){!0===t&&this.version++}});var qe={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},Xe={h:0,s:0,l:0},Ye={h:0,s:0,l:0};function Ze(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+6*(e-t)*(2/3-n):t}function Je(t){return t<.04045?.0773993808*t:Math.pow(.9478672986*t+.0521327014,2.4)}function Qe(t){return t<.0031308?12.92*t:1.055*Math.pow(t,.41666)-.055}var Ke=function(){function t(t,e,n){return void 0===e&&void 0===n?this.set(t):this.setRGB(t,e,n)}var e=t.prototype;return e.set=function(t){return t&&t.isColor?this.copy(t):"number"==typeof t?this.setHex(t):"string"==typeof t&&this.setStyle(t),this},e.setScalar=function(t){return this.r=t,this.g=t,this.b=t,this},e.setHex=function(t){return t=Math.floor(t),this.r=(t>>16&255)/255,this.g=(t>>8&255)/255,this.b=(255&t)/255,this},e.setRGB=function(t,e,n){return this.r=t,this.g=e,this.b=n,this},e.setHSL=function(t,e,n){if(t=gt.euclideanModulo(t,1),e=gt.clamp(e,0,1),n=gt.clamp(n,0,1),0===e)this.r=this.g=this.b=n;else{var r=n<=.5?n*(1+e):n+e-n*e,i=2*n-r;this.r=Ze(i,r,t+1/3),this.g=Ze(i,r,t),this.b=Ze(i,r,t-1/3)}return this},e.setStyle=function(t){function e(e){void 0!==e&&parseFloat(e)<1&&console.warn("THREE.Color: Alpha component of "+t+" will be ignored.")}var n;if(n=/^((?:rgb|hsl)a?)\(([^\)]*)\)/.exec(t)){var r,i=n[1],a=n[2];switch(i){case"rgb":case"rgba":if(r=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return this.r=Math.min(255,parseInt(r[1],10))/255,this.g=Math.min(255,parseInt(r[2],10))/255,this.b=Math.min(255,parseInt(r[3],10))/255,e(r[4]),this;if(r=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return this.r=Math.min(100,parseInt(r[1],10))/100,this.g=Math.min(100,parseInt(r[2],10))/100,this.b=Math.min(100,parseInt(r[3],10))/100,e(r[4]),this;break;case"hsl":case"hsla":if(r=/^\s*(\d*\.?\d+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a)){var o=parseFloat(r[1])/360,s=parseInt(r[2],10)/100,l=parseInt(r[3],10)/100;return e(r[4]),this.setHSL(o,s,l)}}}else if(n=/^\#([A-Fa-f\d]+)$/.exec(t)){var c=n[1],u=c.length;if(3===u)return this.r=parseInt(c.charAt(0)+c.charAt(0),16)/255,this.g=parseInt(c.charAt(1)+c.charAt(1),16)/255,this.b=parseInt(c.charAt(2)+c.charAt(2),16)/255,this;if(6===u)return this.r=parseInt(c.charAt(0)+c.charAt(1),16)/255,this.g=parseInt(c.charAt(2)+c.charAt(3),16)/255,this.b=parseInt(c.charAt(4)+c.charAt(5),16)/255,this}return t&&t.length>0?this.setColorName(t):this},e.setColorName=function(t){var e=qe[t];return void 0!==e?this.setHex(e):console.warn("THREE.Color: Unknown color "+t),this},e.clone=function(){return new this.constructor(this.r,this.g,this.b)},e.copy=function(t){return this.r=t.r,this.g=t.g,this.b=t.b,this},e.copyGammaToLinear=function(t,e){return void 0===e&&(e=2),this.r=Math.pow(t.r,e),this.g=Math.pow(t.g,e),this.b=Math.pow(t.b,e),this},e.copyLinearToGamma=function(t,e){void 0===e&&(e=2);var n=e>0?1/e:1;return this.r=Math.pow(t.r,n),this.g=Math.pow(t.g,n),this.b=Math.pow(t.b,n),this},e.convertGammaToLinear=function(t){return this.copyGammaToLinear(this,t),this},e.convertLinearToGamma=function(t){return this.copyLinearToGamma(this,t),this},e.copySRGBToLinear=function(t){return this.r=Je(t.r),this.g=Je(t.g),this.b=Je(t.b),this},e.copyLinearToSRGB=function(t){return this.r=Qe(t.r),this.g=Qe(t.g),this.b=Qe(t.b),this},e.convertSRGBToLinear=function(){return this.copySRGBToLinear(this),this},e.convertLinearToSRGB=function(){return this.copyLinearToSRGB(this),this},e.getHex=function(){return 255*this.r<<16^255*this.g<<8^255*this.b<<0},e.getHexString=function(){return("000000"+this.getHex().toString(16)).slice(-6)},e.getHSL=function(t){void 0===t&&(console.warn("THREE.Color: .getHSL() target is now required"),t={h:0,s:0,l:0});var e,n,r=this.r,i=this.g,a=this.b,o=Math.max(r,i,a),s=Math.min(r,i,a),l=(s+o)/2;if(s===o)e=0,n=0;else{var c=o-s;switch(n=l<=.5?c/(o+s):c/(2-o-s),o){case r:e=(i-a)/c+(ie&&(e=t[n]);return e}Object.defineProperty(nn.prototype,"needsUpdate",{set:function(t){!0===t&&this.version++}}),Object.assign(nn.prototype,{isBufferAttribute:!0,onUploadCallback:function(){},setUsage:function(t){return this.usage=t,this},copy:function(t){return this.name=t.name,this.array=new t.array.constructor(t.array),this.itemSize=t.itemSize,this.count=t.count,this.normalized=t.normalized,this.usage=t.usage,this},copyAt:function(t,e,n){t*=this.itemSize,n*=e.itemSize;for(var r=0,i=this.itemSize;r65535?un:ln)(t,1):this.index=t,this},getAttribute:function(t){return this.attributes[t]},setAttribute:function(t,e){return this.attributes[t]=e,this},deleteAttribute:function(t){return delete this.attributes[t],this},hasAttribute:function(t){return void 0!==this.attributes[t]},addGroup:function(t,e,n){void 0===n&&(n=0),this.groups.push({start:t,count:e,materialIndex:n})},clearGroups:function(){this.groups=[]},setDrawRange:function(t,e){this.drawRange.start=t,this.drawRange.count=e},applyMatrix4:function(t){var e=this.attributes.position;void 0!==e&&(e.applyMatrix4(t),e.needsUpdate=!0);var n=this.attributes.normal;if(void 0!==n){var r=(new _t).getNormalMatrix(t);n.applyNormalMatrix(r),n.needsUpdate=!0}var i=this.attributes.tangent;return void 0!==i&&(i.transformDirection(t),i.needsUpdate=!0),null!==this.boundingBox&&this.computeBoundingBox(),null!==this.boundingSphere&&this.computeBoundingSphere(),this},rotateX:function(t){return yn.makeRotationX(t),this.applyMatrix4(yn),this},rotateY:function(t){return yn.makeRotationY(t),this.applyMatrix4(yn),this},rotateZ:function(t){return yn.makeRotationZ(t),this.applyMatrix4(yn),this},translate:function(t,e,n){return yn.makeTranslation(t,e,n),this.applyMatrix4(yn),this},scale:function(t,e,n){return yn.makeScale(t,e,n),this.applyMatrix4(yn),this},lookAt:function(t){return xn.lookAt(t),xn.updateMatrix(),this.applyMatrix4(xn.matrix),this},center:function(){return this.computeBoundingBox(),this.boundingBox.getCenter(_n).negate(),this.translate(_n.x,_n.y,_n.z),this},setFromPoints:function(t){for(var e=[],n=0,r=t.length;n0&&(t.userData=this.userData),void 0!==this.parameters){var e=this.parameters;for(var n in e)void 0!==e[n]&&(t[n]=e[n]);return t}t.data={attributes:{}};var r=this.index;null!==r&&(t.data.index={type:r.array.constructor.name,array:Array.prototype.slice.call(r.array)});var i=this.attributes;for(var a in i){var o=i[a],s=o.toJSON(t.data);""!==o.name&&(s.name=o.name),t.data.attributes[a]=s}var l={},c=!1;for(var u in this.morphAttributes){for(var h=this.morphAttributes[u],d=[],p=0,f=h.length;p0&&(l[u]=d,c=!0)}c&&(t.data.morphAttributes=l,t.data.morphTargetsRelative=this.morphTargetsRelative);var g=this.groups;g.length>0&&(t.data.groups=JSON.parse(JSON.stringify(g)));var y=this.boundingSphere;return null!==y&&(t.data.boundingSphere={center:y.center.toArray(),radius:y.radius}),t},clone:function(){return(new Sn).copy(this)},copy:function(t){this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null;var e={};this.name=t.name;var n=t.index;null!==n&&this.setIndex(n.clone(e));var r=t.attributes;for(var i in r){var a=r[i];this.setAttribute(i,a.clone(e))}var o=t.morphAttributes;for(var s in o){for(var l=[],c=o[s],u=0,h=c.length;un.far?null:{distance:l,point:Un.clone(),object:t}}(t,e,n,r,Ln,Rn,Cn,Gn);if(g){s&&(zn.fromBufferAttribute(s,c),Fn.fromBufferAttribute(s,u),Hn.fromBufferAttribute(s,h),g.uv=Ve.getUV(Gn,Ln,Rn,Cn,zn,Fn,Hn,new yt)),l&&(zn.fromBufferAttribute(l,c),Fn.fromBufferAttribute(l,u),Hn.fromBufferAttribute(l,h),g.uv2=Ve.getUV(Gn,Ln,Rn,Cn,zn,Fn,Hn,new yt));var y={a:c,b:u,c:h,normal:new Rt,materialIndex:0};Ve.getNormal(Ln,Rn,Cn,y.normal),g.face=y}return g}kn.prototype=Object.assign(Object.create(Le.prototype),{constructor:kn,isMesh:!0,copy:function(t){return Le.prototype.copy.call(this,t),void 0!==t.morphTargetInfluences&&(this.morphTargetInfluences=t.morphTargetInfluences.slice()),void 0!==t.morphTargetDictionary&&(this.morphTargetDictionary=Object.assign({},t.morphTargetDictionary)),this.material=t.material,this.geometry=t.geometry,this},updateMorphTargets:function(){var t=this.geometry;if(t.isBufferGeometry){var e=t.morphAttributes,n=Object.keys(e);if(n.length>0){var r=e[n[0]];if(void 0!==r){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(var i=0,a=r.length;i0&&console.error("THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.")}},raycast:function(t,e){var n,r=this.geometry,i=this.material,a=this.matrixWorld;if(void 0!==i&&(null===r.boundingSphere&&r.computeBoundingSphere(),An.copy(r.boundingSphere),An.applyMatrix4(a),!1!==t.ray.intersectsSphere(An)&&(Tn.copy(a).invert(),En.copy(t.ray).applyMatrix4(Tn),null===r.boundingBox||!1!==En.intersectsBox(r.boundingBox))))if(r.isBufferGeometry){var o=r.index,s=r.attributes.position,l=r.morphAttributes.position,c=r.morphTargetsRelative,u=r.attributes.uv,h=r.attributes.uv2,d=r.groups,p=r.drawRange;if(null!==o)if(Array.isArray(i))for(var f=0,m=d.length;f0?1:-1,h.push(A.x,A.y,A.z),d.push(C/m),d.push(1-L/v),T+=1}for(var D=0;D0&&(e.defines=this.defines),e.vertexShader=this.vertexShader,e.fragmentShader=this.fragmentShader;var i={};for(var a in this.extensions)!0===this.extensions[a]&&(i[a]=!0);return Object.keys(i).length>0&&(e.extensions=i),e},Zn.prototype=Object.assign(Object.create(Le.prototype),{constructor:Zn,isCamera:!0,copy:function(t,e){return Le.prototype.copy.call(this,t,e),this.matrixWorldInverse.copy(t.matrixWorldInverse),this.projectionMatrix.copy(t.projectionMatrix),this.projectionMatrixInverse.copy(t.projectionMatrixInverse),this},getWorldDirection:function(t){void 0===t&&(console.warn("THREE.Camera: .getWorldDirection() target is now required"),t=new Rt),this.updateWorldMatrix(!0,!1);var e=this.matrixWorld.elements;return t.set(-e[8],-e[9],-e[10]).normalize()},updateMatrixWorld:function(t){Le.prototype.updateMatrixWorld.call(this,t),this.matrixWorldInverse.copy(this.matrixWorld).invert()},updateWorldMatrix:function(t,e){Le.prototype.updateWorldMatrix.call(this,t,e),this.matrixWorldInverse.copy(this.matrixWorld).invert()},clone:function(){return(new this.constructor).copy(this)}}),Jn.prototype=Object.assign(Object.create(Zn.prototype),{constructor:Jn,isPerspectiveCamera:!0,copy:function(t,e){return Zn.prototype.copy.call(this,t,e),this.fov=t.fov,this.zoom=t.zoom,this.near=t.near,this.far=t.far,this.focus=t.focus,this.aspect=t.aspect,this.view=null===t.view?null:Object.assign({},t.view),this.filmGauge=t.filmGauge,this.filmOffset=t.filmOffset,this},setFocalLength:function(t){var e=.5*this.getFilmHeight()/t;this.fov=2*gt.RAD2DEG*Math.atan(e),this.updateProjectionMatrix()},getFocalLength:function(){var t=Math.tan(.5*gt.DEG2RAD*this.fov);return.5*this.getFilmHeight()/t},getEffectiveFOV:function(){return 2*gt.RAD2DEG*Math.atan(Math.tan(.5*gt.DEG2RAD*this.fov)/this.zoom)},getFilmWidth:function(){return this.filmGauge*Math.min(this.aspect,1)},getFilmHeight:function(){return this.filmGauge/Math.max(this.aspect,1)},setViewOffset:function(t,e,n,r,i,a){this.aspect=t/e,null===this.view&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=t,this.view.fullHeight=e,this.view.offsetX=n,this.view.offsetY=r,this.view.width=i,this.view.height=a,this.updateProjectionMatrix()},clearViewOffset:function(){null!==this.view&&(this.view.enabled=!1),this.updateProjectionMatrix()},updateProjectionMatrix:function(){var t=this.near,e=t*Math.tan(.5*gt.DEG2RAD*this.fov)/this.zoom,n=2*e,r=this.aspect*n,i=-.5*r,a=this.view;if(null!==this.view&&this.view.enabled){var o=a.fullWidth,s=a.fullHeight;i+=a.offsetX*r/o,e-=a.offsetY*n/s,r*=a.width/o,n*=a.height/s}var l=this.filmOffset;0!==l&&(i+=t*l/this.getFilmWidth()),this.projectionMatrix.makePerspective(i,i+r,e,e-n,t,this.far),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()},toJSON:function(t){var e=Le.prototype.toJSON.call(this,t);return e.object.fov=this.fov,e.object.zoom=this.zoom,e.object.near=this.near,e.object.far=this.far,e.object.focus=this.focus,e.object.aspect=this.aspect,null!==this.view&&(e.object.view=Object.assign({},this.view)),e.object.filmGauge=this.filmGauge,e.object.filmOffset=this.filmOffset,e}});var Qn=90,Kn=function(t){function e(e,n,r){var i;if((i=t.call(this)||this).type="CubeCamera",!0!==r.isWebGLCubeRenderTarget)return console.error("THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter."),ut(i);i.renderTarget=r;var a=new Jn(Qn,1,e,n);a.layers=i.layers,a.up.set(0,-1,0),a.lookAt(new Rt(1,0,0)),i.add(a);var o=new Jn(Qn,1,e,n);o.layers=i.layers,o.up.set(0,-1,0),o.lookAt(new Rt(-1,0,0)),i.add(o);var s=new Jn(Qn,1,e,n);s.layers=i.layers,s.up.set(0,0,1),s.lookAt(new Rt(0,1,0)),i.add(s);var l=new Jn(Qn,1,e,n);l.layers=i.layers,l.up.set(0,0,-1),l.lookAt(new Rt(0,-1,0)),i.add(l);var c=new Jn(Qn,1,e,n);c.layers=i.layers,c.up.set(0,-1,0),c.lookAt(new Rt(0,0,1)),i.add(c);var u=new Jn(Qn,1,e,n);return u.layers=i.layers,u.up.set(0,-1,0),u.lookAt(new Rt(0,0,-1)),i.add(u),i}return lt(e,t),e.prototype.update=function(t,e){null===this.parent&&this.updateMatrixWorld();var n=this.renderTarget,r=this.children,i=r[0],a=r[1],o=r[2],s=r[3],l=r[4],c=r[5],u=t.xr.enabled,h=t.getRenderTarget();t.xr.enabled=!1;var d=n.texture.generateMipmaps;n.texture.generateMipmaps=!1,t.setRenderTarget(n,0),t.render(e,i),t.setRenderTarget(n,1),t.render(e,a),t.setRenderTarget(n,2),t.render(e,o),t.setRenderTarget(n,3),t.render(e,s),t.setRenderTarget(n,4),t.render(e,l),n.texture.generateMipmaps=d,t.setRenderTarget(n,5),t.render(e,c),t.setRenderTarget(h),t.xr.enabled=u},e}(Le),$n=function(t){function e(e,n,r,i,o,s,l,c,u,h){var d;return e=void 0!==e?e:[],n=void 0!==n?n:a,l=void 0!==l?l:E,(d=t.call(this,e,n,r,i,o,s,l,c,u,h)||this)._needsFlipEnvMap=!0,d.flipY=!1,d}return lt(e,t),st(e,[{key:"images",get:function(){return this.image},set:function(t){this.image=t}}]),e}(Mt);$n.prototype.isCubeTexture=!0;var tr=function(t){function e(e,n,r){var i;return Number.isInteger(n)&&(console.warn("THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )"),n=r),i=t.call(this,e,e,n)||this,n=n||{},i.texture=new $n(void 0,n.mapping,n.wrapS,n.wrapT,n.magFilter,n.minFilter,n.format,n.type,n.anisotropy,n.encoding),i.texture.generateMipmaps=void 0!==n.generateMipmaps&&n.generateMipmaps,i.texture.minFilter=void 0!==n.minFilter?n.minFilter:g,i.texture._needsFlipEnvMap=!1,i}lt(e,t);var n=e.prototype;return n.fromEquirectangularTexture=function(t,e){this.texture.type=e.type,this.texture.format=A,this.texture.encoding=e.encoding,this.texture.generateMipmaps=e.generateMipmaps,this.texture.minFilter=e.minFilter,this.texture.magFilter=e.magFilter;var n={tEquirect:{value:null}},r="\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\tvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\n\t\t\t\t\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n\n\t\t\t\t}\n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvWorldDirection = transformDirection( position, modelMatrix );\n\n\t\t\t\t\t#include \n\t\t\t\t\t#include \n\n\t\t\t\t}\n\t\t\t",i="\n\n\t\t\t\tuniform sampler2D tEquirect;\n\n\t\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t\t#include \n\n\t\t\t\tvoid main() {\n\n\t\t\t\t\tvec3 direction = normalize( vWorldDirection );\n\n\t\t\t\t\tvec2 sampleUV = equirectUv( direction );\n\n\t\t\t\t\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\n\t\t\t\t}\n\t\t\t",a=new Wn(5,5,5),o=new Yn({name:"CubemapFromEquirect",uniforms:jn(n),vertexShader:r,fragmentShader:i,side:1,blending:0});o.uniforms.tEquirect.value=e;var s=new kn(a,o),l=e.minFilter;return e.minFilter===x&&(e.minFilter=g),new Kn(1,10,this).update(t,s),e.minFilter=l,s.geometry.dispose(),s.material.dispose(),this},n.clear=function(t,e,n,r){for(var i=t.getRenderTarget(),a=0;a<6;a++)t.setRenderTarget(this,a),t.clear(e,n,r);t.setRenderTarget(i)},e}(Et);tr.prototype.isWebGLCubeRenderTarget=!0;var er=function(t){function e(e,n,r,i,a,o,s,l,c,u,h,d){var p;return(p=t.call(this,null,o,s,l,c,u,i,a,h,d)||this).image={data:e||null,width:n||1,height:r||1},p.magFilter=void 0!==c?c:f,p.minFilter=void 0!==u?u:f,p.generateMipmaps=!1,p.flipY=!1,p.unpackAlignment=1,p.needsUpdate=!0,p}return lt(e,t),e}(Mt);er.prototype.isDataTexture=!0;var nr=new Yt,rr=new Rt,ir=function(){function t(t,e,n,r,i,a){void 0===t&&(t=new De),void 0===e&&(e=new De),void 0===n&&(n=new De),void 0===r&&(r=new De),void 0===i&&(i=new De),void 0===a&&(a=new De),this.planes=[t,e,n,r,i,a]}var e=t.prototype;return e.set=function(t,e,n,r,i,a){var o=this.planes;return o[0].copy(t),o[1].copy(e),o[2].copy(n),o[3].copy(r),o[4].copy(i),o[5].copy(a),this},e.copy=function(t){for(var e=this.planes,n=0;n<6;n++)e[n].copy(t.planes[n]);return this},e.setFromProjectionMatrix=function(t){var e=this.planes,n=t.elements,r=n[0],i=n[1],a=n[2],o=n[3],s=n[4],l=n[5],c=n[6],u=n[7],h=n[8],d=n[9],p=n[10],f=n[11],m=n[12],v=n[13],g=n[14],y=n[15];return e[0].setComponents(o-r,u-s,f-h,y-m).normalize(),e[1].setComponents(o+r,u+s,f+h,y+m).normalize(),e[2].setComponents(o+i,u+l,f+d,y+v).normalize(),e[3].setComponents(o-i,u-l,f-d,y-v).normalize(),e[4].setComponents(o-a,u-c,f-p,y-g).normalize(),e[5].setComponents(o+a,u+c,f+p,y+g).normalize(),this},e.intersectsObject=function(t){var e=t.geometry;return null===e.boundingSphere&&e.computeBoundingSphere(),nr.copy(e.boundingSphere).applyMatrix4(t.matrixWorld),this.intersectsSphere(nr)},e.intersectsSprite=function(t){return nr.center.set(0,0,0),nr.radius=.7071067811865476,nr.applyMatrix4(t.matrixWorld),this.intersectsSphere(nr)},e.intersectsSphere=function(t){for(var e=this.planes,n=t.center,r=-t.radius,i=0;i<6;i++){if(e[i].distanceToPoint(n)0?t.max.x:t.min.x,rr.y=r.normal.y>0?t.max.y:t.min.y,rr.z=r.normal.z>0?t.max.z:t.min.z,r.distanceToPoint(rr)<0)return!1}return!0},e.containsPoint=function(t){for(var e=this.planes,n=0;n<6;n++)if(e[n].distanceToPoint(t)<0)return!1;return!0},e.clone=function(){return(new this.constructor).copy(this)},t}();function ar(){var t=null,e=!1,n=null,r=null;function i(e,a){n(e,a),r=t.requestAnimationFrame(i)}return{start:function(){!0!==e&&null!==n&&(r=t.requestAnimationFrame(i),e=!0)},stop:function(){t.cancelAnimationFrame(r),e=!1},setAnimationLoop:function(t){n=t},setContext:function(e){t=e}}}function or(t,e){var n=e.isWebGL2,r=new WeakMap;return{get:function(t){return t.isInterleavedBufferAttribute&&(t=t.data),r.get(t)},remove:function(e){e.isInterleavedBufferAttribute&&(e=e.data);var n=r.get(e);n&&(t.deleteBuffer(n.buffer),r.delete(e))},update:function(e,i){if(e.isGLBufferAttribute){var a=r.get(e);(!a||a.version 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif",bumpmap_pars_fragment:"#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 ) * faceDirection;\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif",clipping_planes_fragment:"#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif",clipping_planes_pars_fragment:"#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif",clipping_planes_pars_vertex:"#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n#endif",clipping_planes_vertex:"#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif",color_fragment:"#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif",color_pars_fragment:"#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif",color_pars_vertex:"#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif",color_vertex:"#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor.xyz *= color.xyz;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif",common:"#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}",cube_uv_reflection_fragment:"#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_maxMipLevel 8.0\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_maxTileSize 256.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tfloat texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 );\n\t\tvec2 f = fract( uv );\n\t\tuv += 0.5 - f;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tif ( mipInt < cubeUV_maxMipLevel ) {\n\t\t\tuv.y += 2.0 * cubeUV_maxTileSize;\n\t\t}\n\t\tuv.y += filterInt * 2.0 * cubeUV_minTileSize;\n\t\tuv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize );\n\t\tuv *= texelSize;\n\t\tvec3 tl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x += texelSize;\n\t\tvec3 tr = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.y += texelSize;\n\t\tvec3 br = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x -= texelSize;\n\t\tvec3 bl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tvec3 tm = mix( tl, tr, f.x );\n\t\tvec3 bm = mix( bl, br, f.x );\n\t\treturn mix( tm, bm, f.y );\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif",defaultnormal_vertex:"vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif",displacementmap_pars_vertex:"#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif",displacementmap_vertex:"#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias );\n#endif",emissivemap_fragment:"#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif",emissivemap_pars_fragment:"#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif",encodings_fragment:"gl_FragColor = linearToOutputTexel( gl_FragColor );",encodings_pars_fragment:"\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}",envmap_fragment:"#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif",envmap_common_pars_fragment:"#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif",envmap_pars_fragment:"#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif",envmap_pars_vertex:"#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif",envmap_physical_pars_fragment:"#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif",envmap_vertex:"#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif",fog_vertex:"#ifdef USE_FOG\n\tfogDepth = - mvPosition.z;\n#endif",fog_pars_vertex:"#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif",fog_fragment:"#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif",fog_pars_fragment:"#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif",gradientmap_pars_fragment:"#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}",lightmap_fragment:"#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\treflectedLight.indirectDiffuse += PI * lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n#endif",lightmap_pars_fragment:"#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif",lights_lambert_vertex:"vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif",lights_pars_begin:"uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif",lights_toon_fragment:"ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;",lights_toon_pars_fragment:"varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)",lights_phong_fragment:"BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;",lights_phong_pars_fragment:"varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)",lights_physical_fragment:"PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.specularRoughness = max( roughnessFactor, 0.0525 );material.specularRoughness += geometryRoughness;\nmaterial.specularRoughness = min( material.specularRoughness, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif",lights_physical_pars_fragment:"struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat specularRoughness;\n\tvec3 specularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearcoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3(\t\t0, 1,\t\t0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}",lights_fragment_begin:"\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif",lights_fragment_maps:"#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif",lights_fragment_end:"#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif",logdepthbuf_fragment:"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif",logdepthbuf_pars_fragment:"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n#endif",logdepthbuf_pars_vertex:"#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif",logdepthbuf_vertex:"#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif",map_fragment:"#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif",map_pars_fragment:"#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif",map_particle_fragment:"#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif",map_particle_pars_fragment:"#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tuniform mat3 uvTransform;\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif",metalnessmap_fragment:"float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif",metalnessmap_pars_fragment:"#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif",morphnormal_vertex:"#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif",morphtarget_pars_vertex:"#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\t\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\t\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif",morphtarget_vertex:"#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif",normal_fragment_begin:"float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;\n#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * faceDirection;\n\t\t\tbitangent = bitangent * faceDirection;\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;",normal_fragment_maps:"#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, mapN, faceDirection );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd(), faceDirection );\n#endif",normalmap_pars_fragment:"#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tvec3 N = surf_norm;\n\t\tvec3 q1perp = cross( q1, N );\n\t\tvec3 q0perp = cross( N, q0 );\n\t\tvec3 T = q1perp * st0.x + q0perp * st1.x;\n\t\tvec3 B = q1perp * st0.y + q0perp * st1.y;\n\t\tfloat det = max( dot( T, T ), dot( B, B ) );\n\t\tfloat scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det );\n\t\treturn normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z );\n\t}\n#endif",clearcoat_normal_fragment_begin:"#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif",clearcoat_normal_fragment_maps:"#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN, faceDirection );\n\t#endif\n#endif",clearcoat_pars_fragment:"#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif",packing:"vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}",premultiplied_alpha_fragment:"#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif",project_vertex:"vec4 mvPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\n\tmvPosition = instanceMatrix * mvPosition;\n#endif\nmvPosition = modelViewMatrix * mvPosition;\ngl_Position = projectionMatrix * mvPosition;",dithering_fragment:"#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif",dithering_pars_fragment:"#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif",roughnessmap_fragment:"float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif",roughnessmap_pars_fragment:"#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif",shadowmap_pars_fragment:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t\tf.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), \n\t\t\t\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t\tf.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif",shadowmap_pars_vertex:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif",shadowmap_vertex:"#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0\n\t\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\tvec4 shadowWorldPosition;\n\t#endif\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n#endif",shadowmask_pars_fragment:"float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}",skinbase_vertex:"#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif",skinning_pars_vertex:"#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif",skinning_vertex:"#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif",skinnormal_vertex:"#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif",specularmap_fragment:"float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif",specularmap_pars_fragment:"#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif",tonemapping_fragment:"#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif",tonemapping_pars_fragment:"#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3(\t1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108,\t1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605,\t1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }",transmissionmap_fragment:"#ifdef USE_TRANSMISSIONMAP\n\ttotalTransmission *= texture2D( transmissionMap, vUv ).r;\n#endif",transmissionmap_pars_fragment:"#ifdef USE_TRANSMISSIONMAP\n\tuniform sampler2D transmissionMap;\n#endif",uv_pars_fragment:"#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif",uv_pars_vertex:"#ifdef USE_UV\n\t#ifdef UVS_VERTEX_ONLY\n\t\tvec2 vUv;\n\t#else\n\t\tvarying vec2 vUv;\n\t#endif\n\tuniform mat3 uvTransform;\n#endif",uv_vertex:"#ifdef USE_UV\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif",uv2_pars_fragment:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif",uv2_pars_vertex:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n\tuniform mat3 uv2Transform;\n#endif",uv2_vertex:"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif",worldpos_vertex:"#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif",background_frag:"uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}",background_vert:"varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}",cube_frag:"#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}",cube_vert:"varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}",depth_frag:"#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}",depth_vert:"#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}",distanceRGBA_frag:"#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}",distanceRGBA_vert:"#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}",equirect_frag:"uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}",equirect_vert:"varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}",linedashed_frag:"uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}",linedashed_vert:"uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshbasic_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshbasic_vert:"#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshlambert_frag:"uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshlambert_vert:"#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshmatcap_frag:"#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshmatcap_vert:"#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}",meshtoon_frag:"#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshtoon_vert:"#define TOON\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}",meshphong_frag:"#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshphong_vert:"#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}",meshphysical_frag:"#define STANDARD\n#ifdef PHYSICAL\n\t#define REFLECTIVITY\n\t#define CLEARCOAT\n\t#define TRANSMISSION\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef TRANSMISSION\n\tuniform float transmission;\n#endif\n#ifdef REFLECTIVITY\n\tuniform float reflectivity;\n#endif\n#ifdef CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheen;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#ifdef TRANSMISSION\n\t\tfloat totalTransmission = transmission;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#ifdef TRANSMISSION\n\t\tdiffuseColor.a *= mix( saturate( 1. - totalTransmission + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) ), 1.0, metalness );\n\t#endif\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",meshphysical_vert:"#define STANDARD\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}",normal_frag:"#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}",normal_vert:"#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}",points_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}",points_vert:"uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}",shadow_frag:"uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}",shadow_vert:"#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}",sprite_frag:"uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n}",sprite_vert:"uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"},cr={common:{diffuse:{value:new Ke(15658734)},opacity:{value:1},map:{value:null},uvTransform:{value:new _t},uv2Transform:{value:new _t},alphaMap:{value:null}},specularmap:{specularMap:{value:null}},envmap:{envMap:{value:null},flipEnvMap:{value:-1},reflectivity:{value:1},refractionRatio:{value:.98},maxMipLevel:{value:0}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1}},emissivemap:{emissiveMap:{value:null}},bumpmap:{bumpMap:{value:null},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalScale:{value:new yt(1,1)}},displacementmap:{displacementMap:{value:null},displacementScale:{value:1},displacementBias:{value:0}},roughnessmap:{roughnessMap:{value:null}},metalnessmap:{metalnessMap:{value:null}},gradientmap:{gradientMap:{value:null}},fog:{fogDensity:{value:25e-5},fogNear:{value:1},fogFar:{value:2e3},fogColor:{value:new Ke(16777215)}},lights:{ambientLightColor:{value:[]},lightProbe:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{}}},directionalLightShadows:{value:[],properties:{shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMap:{value:[]},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{}}},spotLightShadows:{value:[],properties:{shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},spotShadowMap:{value:[]},spotShadowMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{}}},pointLightShadows:{value:[],properties:{shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{},shadowCameraNear:{},shadowCameraFar:{}}},pointShadowMap:{value:[]},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}},rectAreaLights:{value:[],properties:{color:{},position:{},width:{},height:{}}},ltc_1:{value:null},ltc_2:{value:null}},points:{diffuse:{value:new Ke(15658734)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},uvTransform:{value:new _t}},sprite:{diffuse:{value:new Ke(15658734)},opacity:{value:1},center:{value:new yt(.5,.5)},rotation:{value:0},map:{value:null},alphaMap:{value:null},uvTransform:{value:new _t}}},ur={basic:{uniforms:qn([cr.common,cr.specularmap,cr.envmap,cr.aomap,cr.lightmap,cr.fog]),vertexShader:lr.meshbasic_vert,fragmentShader:lr.meshbasic_frag},lambert:{uniforms:qn([cr.common,cr.specularmap,cr.envmap,cr.aomap,cr.lightmap,cr.emissivemap,cr.fog,cr.lights,{emissive:{value:new Ke(0)}}]),vertexShader:lr.meshlambert_vert,fragmentShader:lr.meshlambert_frag},phong:{uniforms:qn([cr.common,cr.specularmap,cr.envmap,cr.aomap,cr.lightmap,cr.emissivemap,cr.bumpmap,cr.normalmap,cr.displacementmap,cr.fog,cr.lights,{emissive:{value:new Ke(0)},specular:{value:new Ke(1118481)},shininess:{value:30}}]),vertexShader:lr.meshphong_vert,fragmentShader:lr.meshphong_frag},standard:{uniforms:qn([cr.common,cr.envmap,cr.aomap,cr.lightmap,cr.emissivemap,cr.bumpmap,cr.normalmap,cr.displacementmap,cr.roughnessmap,cr.metalnessmap,cr.fog,cr.lights,{emissive:{value:new Ke(0)},roughness:{value:1},metalness:{value:0},envMapIntensity:{value:1}}]),vertexShader:lr.meshphysical_vert,fragmentShader:lr.meshphysical_frag},toon:{uniforms:qn([cr.common,cr.aomap,cr.lightmap,cr.emissivemap,cr.bumpmap,cr.normalmap,cr.displacementmap,cr.gradientmap,cr.fog,cr.lights,{emissive:{value:new Ke(0)}}]),vertexShader:lr.meshtoon_vert,fragmentShader:lr.meshtoon_frag},matcap:{uniforms:qn([cr.common,cr.bumpmap,cr.normalmap,cr.displacementmap,cr.fog,{matcap:{value:null}}]),vertexShader:lr.meshmatcap_vert,fragmentShader:lr.meshmatcap_frag},points:{uniforms:qn([cr.points,cr.fog]),vertexShader:lr.points_vert,fragmentShader:lr.points_frag},dashed:{uniforms:qn([cr.common,cr.fog,{scale:{value:1},dashSize:{value:1},totalSize:{value:2}}]),vertexShader:lr.linedashed_vert,fragmentShader:lr.linedashed_frag},depth:{uniforms:qn([cr.common,cr.displacementmap]),vertexShader:lr.depth_vert,fragmentShader:lr.depth_frag},normal:{uniforms:qn([cr.common,cr.bumpmap,cr.normalmap,cr.displacementmap,{opacity:{value:1}}]),vertexShader:lr.normal_vert,fragmentShader:lr.normal_frag},sprite:{uniforms:qn([cr.sprite,cr.fog]),vertexShader:lr.sprite_vert,fragmentShader:lr.sprite_frag},background:{uniforms:{uvTransform:{value:new _t},t2D:{value:null}},vertexShader:lr.background_vert,fragmentShader:lr.background_frag},cube:{uniforms:qn([cr.envmap,{opacity:{value:1}}]),vertexShader:lr.cube_vert,fragmentShader:lr.cube_frag},equirect:{uniforms:{tEquirect:{value:null}},vertexShader:lr.equirect_vert,fragmentShader:lr.equirect_frag},distanceRGBA:{uniforms:qn([cr.common,cr.displacementmap,{referencePosition:{value:new Rt},nearDistance:{value:1},farDistance:{value:1e3}}]),vertexShader:lr.distanceRGBA_vert,fragmentShader:lr.distanceRGBA_frag},shadow:{uniforms:qn([cr.lights,cr.fog,{color:{value:new Ke(0)},opacity:{value:1}}]),vertexShader:lr.shadow_vert,fragmentShader:lr.shadow_frag}};function hr(t,e,n,r,i){var a,o,s=new Ke(0),l=0,u=null,h=0,d=null;function p(t,e){n.buffers.color.setClear(t.r,t.g,t.b,e,i)}return{getClearColor:function(){return s},setClearColor:function(t,e){void 0===e&&(e=1),s.set(t),p(s,l=e)},getClearAlpha:function(){return l},setClearAlpha:function(t){p(s,l=t)},render:function(n,i,f,m){var v=!0===i.isScene?i.background:null;v&&v.isTexture&&(v=e.get(v));var g=t.xr,y=g.getSession&&g.getSession();y&&"additive"===y.environmentBlendMode&&(v=null),null===v?p(s,l):v&&v.isColor&&(p(v,1),m=!0),(t.autoClear||m)&&t.clear(t.autoClearColor,t.autoClearDepth,t.autoClearStencil),v&&(v.isCubeTexture||v.isWebGLCubeRenderTarget||v.mapping===c)?(void 0===o&&((o=new kn(new Wn(1,1,1),new Yn({name:"BackgroundCubeMaterial",uniforms:jn(ur.cube.uniforms),vertexShader:ur.cube.vertexShader,fragmentShader:ur.cube.fragmentShader,side:1,depthTest:!1,depthWrite:!1,fog:!1}))).geometry.deleteAttribute("normal"),o.geometry.deleteAttribute("uv"),o.onBeforeRender=function(t,e,n){this.matrixWorld.copyPosition(n.matrixWorld)},Object.defineProperty(o.material,"envMap",{get:function(){return this.uniforms.envMap.value}}),r.update(o)),v.isWebGLCubeRenderTarget&&(v=v.texture),o.material.uniforms.envMap.value=v,o.material.uniforms.flipEnvMap.value=v.isCubeTexture&&v._needsFlipEnvMap?-1:1,u===v&&h===v.version&&d===t.toneMapping||(o.material.needsUpdate=!0,u=v,h=v.version,d=t.toneMapping),n.unshift(o,o.geometry,o.material,0,0,null)):v&&v.isTexture&&(void 0===a&&((a=new kn(new sr(2,2),new Yn({name:"BackgroundMaterial",uniforms:jn(ur.background.uniforms),vertexShader:ur.background.vertexShader,fragmentShader:ur.background.fragmentShader,side:0,depthTest:!1,depthWrite:!1,fog:!1}))).geometry.deleteAttribute("normal"),Object.defineProperty(a.material,"map",{get:function(){return this.uniforms.t2D.value}}),r.update(a)),a.material.uniforms.t2D.value=v,!0===v.matrixAutoUpdate&&v.updateMatrix(),a.material.uniforms.uvTransform.value.copy(v.matrix),u===v&&h===v.version&&d===t.toneMapping||(a.material.needsUpdate=!0,u=v,h=v.version,d=t.toneMapping),n.unshift(a,a.geometry,a.material,0,0,null))}}}function dr(t,e,n,r){var i=t.getParameter(34921),a=r.isWebGL2?null:e.get("OES_vertex_array_object"),o=r.isWebGL2||null!==a,s={},l=d(null),c=l;function u(e){return r.isWebGL2?t.bindVertexArray(e):a.bindVertexArrayOES(e)}function h(e){return r.isWebGL2?t.deleteVertexArray(e):a.deleteVertexArrayOES(e)}function d(t){for(var e=[],n=[],r=[],a=0;a=0){var y=l[h];if(void 0!==y){var x=y.normalized,_=y.itemSize,b=n.get(y);if(void 0===b)continue;var w=b.buffer,M=b.type,S=b.bytesPerElement;if(y.isInterleavedBufferAttribute){var T=y.data,E=T.stride,A=y.offset;T&&T.isInstancedInterleavedBuffer?(m(d,T.meshPerAttribute),void 0===s._maxInstanceCount&&(s._maxInstanceCount=T.meshPerAttribute*T.count)):f(d),t.bindBuffer(34962,w),g(d,_,M,x,E*S,A*S)}else y.isInstancedBufferAttribute?(m(d,y.meshPerAttribute),void 0===s._maxInstanceCount&&(s._maxInstanceCount=y.meshPerAttribute*y.count)):f(d),t.bindBuffer(34962,w),g(d,_,M,x,0,0)}else if("instanceMatrix"===h){var L=n.get(i.instanceMatrix);if(void 0===L)continue;var R=L.buffer,C=L.type;m(d+0,1),m(d+1,1),m(d+2,1),m(d+3,1),t.bindBuffer(34962,R),t.vertexAttribPointer(d+0,4,C,!1,64,0),t.vertexAttribPointer(d+1,4,C,!1,64,16),t.vertexAttribPointer(d+2,4,C,!1,64,32),t.vertexAttribPointer(d+3,4,C,!1,64,48)}else if("instanceColor"===h){var P=n.get(i.instanceColor);if(void 0===P)continue;var D=P.buffer,I=P.type;m(d,1),t.bindBuffer(34962,D),t.vertexAttribPointer(d,3,I,!1,12,0)}else if(void 0!==u){var N=u[h];if(void 0!==N)switch(N.length){case 2:t.vertexAttrib2fv(d,N);break;case 3:t.vertexAttrib3fv(d,N);break;case 4:t.vertexAttrib4fv(d,N);break;default:t.vertexAttrib1fv(d,N)}}}}v()}(i,l,h,y),null!==x&&t.bindBuffer(34963,n.get(x).buffer))},reset:y,resetDefaultState:x,dispose:function(){for(var t in y(),s){var e=s[t];for(var n in e){var r=e[n];for(var i in r)h(r[i].object),delete r[i];delete e[n]}delete s[t]}},releaseStatesOfGeometry:function(t){if(void 0!==s[t.id]){var e=s[t.id];for(var n in e){var r=e[n];for(var i in r)h(r[i].object),delete r[i];delete e[n]}delete s[t.id]}},releaseStatesOfProgram:function(t){for(var e in s){var n=s[e];if(void 0!==n[t.id]){var r=n[t.id];for(var i in r)h(r[i].object),delete r[i];delete n[t.id]}}},initAttributes:p,enableAttribute:f,disableUnusedAttributes:v}}function pr(t,e,n,r){var i,a=r.isWebGL2;this.setMode=function(t){i=t},this.render=function(e,r){t.drawArrays(i,e,r),n.update(r,i,1)},this.renderInstances=function(r,o,s){if(0!==s){var l,c;if(a)l=t,c="drawArraysInstanced";else if(c="drawArraysInstancedANGLE",null===(l=e.get("ANGLE_instanced_arrays")))return void console.error("THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.");l[c](i,r,o,s),n.update(o,i,s)}}}function fr(t,e,n){var r;function i(e){if("highp"===e){if(t.getShaderPrecisionFormat(35633,36338).precision>0&&t.getShaderPrecisionFormat(35632,36338).precision>0)return"highp";e="mediump"}return"mediump"===e&&t.getShaderPrecisionFormat(35633,36337).precision>0&&t.getShaderPrecisionFormat(35632,36337).precision>0?"mediump":"lowp"}var a="undefined"!=typeof WebGL2RenderingContext&&t instanceof WebGL2RenderingContext||"undefined"!=typeof WebGL2ComputeRenderingContext&&t instanceof WebGL2ComputeRenderingContext,o=void 0!==n.precision?n.precision:"highp",s=i(o);s!==o&&(console.warn("THREE.WebGLRenderer:",o,"not supported, using",s,"instead."),o=s);var l=!0===n.logarithmicDepthBuffer,c=t.getParameter(34930),u=t.getParameter(35660),h=t.getParameter(3379),d=t.getParameter(34076),p=t.getParameter(34921),f=t.getParameter(36347),m=t.getParameter(36348),v=t.getParameter(36349),g=u>0,y=a||e.has("OES_texture_float");return{isWebGL2:a,getMaxAnisotropy:function(){if(void 0!==r)return r;if(!0===e.has("EXT_texture_filter_anisotropic")){var n=e.get("EXT_texture_filter_anisotropic");r=t.getParameter(n.MAX_TEXTURE_MAX_ANISOTROPY_EXT)}else r=0;return r},getMaxPrecision:i,precision:o,logarithmicDepthBuffer:l,maxTextures:c,maxVertexTextures:u,maxTextureSize:h,maxCubemapSize:d,maxAttributes:p,maxVertexUniforms:f,maxVaryings:m,maxFragmentUniforms:v,vertexTextures:g,floatFragmentTextures:y,floatVertexTextures:g&&y,maxSamples:a?t.getParameter(36183):0}}function mr(t){var e=this,n=null,r=0,i=!1,a=!1,o=new De,s=new _t,l={value:null,needsUpdate:!1};function c(){l.value!==n&&(l.value=n,l.needsUpdate=r>0),e.numPlanes=r,e.numIntersection=0}function u(t,n,r,i){var a=null!==t?t.length:0,c=null;if(0!==a){if(c=l.value,!0!==i||null===c){var u=r+4*a,h=n.matrixWorldInverse;s.getNormalMatrix(h),(null===c||c.length0){var c=t.getRenderTarget(),u=new tr(o.height/2);return u.fromEquirectangularTexture(t,i),e.set(i,u),t.setRenderTarget(c),i.addEventListener("dispose",r),n(u.texture,i.mapping)}return null}}return i},dispose:function(){e=new WeakMap}}}function gr(t){var e={};function n(n){if(void 0!==e[n])return e[n];var r;switch(n){case"WEBGL_depth_texture":r=t.getExtension("WEBGL_depth_texture")||t.getExtension("MOZ_WEBGL_depth_texture")||t.getExtension("WEBKIT_WEBGL_depth_texture");break;case"EXT_texture_filter_anisotropic":r=t.getExtension("EXT_texture_filter_anisotropic")||t.getExtension("MOZ_EXT_texture_filter_anisotropic")||t.getExtension("WEBKIT_EXT_texture_filter_anisotropic");break;case"WEBGL_compressed_texture_s3tc":r=t.getExtension("WEBGL_compressed_texture_s3tc")||t.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||t.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");break;case"WEBGL_compressed_texture_pvrtc":r=t.getExtension("WEBGL_compressed_texture_pvrtc")||t.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");break;default:r=t.getExtension(n)}return e[n]=r,r}return{has:function(t){return null!==n(t)},init:function(t){t.isWebGL2?n("EXT_color_buffer_float"):(n("WEBGL_depth_texture"),n("OES_texture_float"),n("OES_texture_half_float"),n("OES_texture_half_float_linear"),n("OES_standard_derivatives"),n("OES_element_index_uint"),n("OES_vertex_array_object"),n("ANGLE_instanced_arrays")),n("OES_texture_float_linear"),n("EXT_color_buffer_half_float")},get:function(t){var e=n(t);return null===e&&console.warn("THREE.WebGLRenderer: "+t+" extension not supported."),e}}}function yr(t,e,n,r){var i={},a=new WeakMap;function o(t){var s=t.target;for(var l in null!==s.index&&e.remove(s.index),s.attributes)e.remove(s.attributes[l]);s.removeEventListener("dispose",o),delete i[s.id];var c=a.get(s);c&&(e.remove(c),a.delete(s)),r.releaseStatesOfGeometry(s),!0===s.isInstancedBufferGeometry&&delete s._maxInstanceCount,n.memory.geometries--}function s(t){var n=[],r=t.index,i=t.attributes.position,o=0;if(null!==r){var s=r.array;o=r.version;for(var l=0,c=s.length;l65535?un:ln)(n,1);x.version=o;var _=a.get(t);_&&e.remove(_),a.set(t,x)}return{get:function(t,e){return!0===i[e.id]||(e.addEventListener("dispose",o),i[e.id]=!0,n.memory.geometries++),e},update:function(t){var n=t.attributes;for(var r in n)e.update(n[r],34962);var i=t.morphAttributes;for(var a in i)for(var o=i[a],s=0,l=o.length;s0)return t;var i=e*n,a=Pr[i];if(void 0===a&&(a=new Float32Array(i),Pr[i]=a),0!==e){r.toArray(a,0);for(var o=1,s=0;o!==e;++o)s+=n,t[o].toArray(a,s)}return a}function zr(t,e){if(t.length!==e.length)return!1;for(var n=0,r=t.length;n/gm;function Di(t){return t.replace(Pi,Ii)}function Ii(t,e){var n=lr[e];if(void 0===n)throw new Error("Can not resolve #include <"+e+">");return Di(n)}var Ni=/#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g,Bi=/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;function Oi(t){return t.replace(Bi,Fi).replace(Ni,zi)}function zi(t,e,n,r){return console.warn("WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead."),Fi(t,e,n,r)}function Fi(t,e,n,r){for(var i="",a=parseInt(e);a0?t.gammaFactor:1,w=n.isWebGL2?"":function(t){return[t.extensionDerivatives||t.envMapCubeUV||t.bumpMap||t.tangentSpaceNormalMap||t.clearcoatNormalMap||t.flatShading||"physical"===t.shaderID?"#extension GL_OES_standard_derivatives : enable":"",(t.extensionFragDepth||t.logarithmicDepthBuffer)&&t.rendererExtensionFragDepth?"#extension GL_EXT_frag_depth : enable":"",t.extensionDrawBuffers&&t.rendererExtensionDrawBuffers?"#extension GL_EXT_draw_buffers : require":"",(t.extensionShaderTextureLOD||t.envMap)&&t.rendererExtensionShaderTextureLod?"#extension GL_EXT_shader_texture_lod : enable":""].filter(Li).join("\n")}(n),M=function(t){var e=[];for(var n in t){var r=t[n];!1!==r&&e.push("#define "+n+" "+r)}return e.join("\n")}(f),S=p.createProgram(),T=n.glslVersion?"#version "+n.glslVersion+"\n":"";n.isRawShaderMaterial?((i=[M].filter(Li).join("\n")).length>0&&(i+="\n"),(s=[w,M].filter(Li).join("\n")).length>0&&(s+="\n")):(i=[Hi(n),"#define SHADER_NAME "+n.shaderName,M,n.instancing?"#define USE_INSTANCING":"",n.instancingColor?"#define USE_INSTANCING_COLOR":"",n.supportsVertexTextures?"#define VERTEX_TEXTURES":"","#define GAMMA_FACTOR "+b,"#define MAX_BONES "+n.maxBones,n.useFog&&n.fog?"#define USE_FOG":"",n.useFog&&n.fogExp2?"#define FOG_EXP2":"",n.map?"#define USE_MAP":"",n.envMap?"#define USE_ENVMAP":"",n.envMap?"#define "+x:"",n.lightMap?"#define USE_LIGHTMAP":"",n.aoMap?"#define USE_AOMAP":"",n.emissiveMap?"#define USE_EMISSIVEMAP":"",n.bumpMap?"#define USE_BUMPMAP":"",n.normalMap?"#define USE_NORMALMAP":"",n.normalMap&&n.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",n.normalMap&&n.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",n.clearcoatMap?"#define USE_CLEARCOATMAP":"",n.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",n.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",n.displacementMap&&n.supportsVertexTextures?"#define USE_DISPLACEMENTMAP":"",n.specularMap?"#define USE_SPECULARMAP":"",n.roughnessMap?"#define USE_ROUGHNESSMAP":"",n.metalnessMap?"#define USE_METALNESSMAP":"",n.alphaMap?"#define USE_ALPHAMAP":"",n.transmissionMap?"#define USE_TRANSMISSIONMAP":"",n.vertexTangents?"#define USE_TANGENT":"",n.vertexColors?"#define USE_COLOR":"",n.vertexUvs?"#define USE_UV":"",n.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",n.flatShading?"#define FLAT_SHADED":"",n.skinning?"#define USE_SKINNING":"",n.useVertexTexture?"#define BONE_TEXTURE":"",n.morphTargets?"#define USE_MORPHTARGETS":"",n.morphNormals&&!1===n.flatShading?"#define USE_MORPHNORMALS":"",n.doubleSided?"#define DOUBLE_SIDED":"",n.flipSided?"#define FLIP_SIDED":"",n.shadowMapEnabled?"#define USE_SHADOWMAP":"",n.shadowMapEnabled?"#define "+g:"",n.sizeAttenuation?"#define USE_SIZEATTENUATION":"",n.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",n.logarithmicDepthBuffer&&n.rendererExtensionFragDepth?"#define USE_LOGDEPTHBUF_EXT":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;","#ifdef USE_INSTANCING","\tattribute mat4 instanceMatrix;","#endif","#ifdef USE_INSTANCING_COLOR","\tattribute vec3 instanceColor;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_TANGENT","\tattribute vec4 tangent;","#endif","#ifdef USE_COLOR","\tattribute vec3 color;","#endif","#ifdef USE_MORPHTARGETS","\tattribute vec3 morphTarget0;","\tattribute vec3 morphTarget1;","\tattribute vec3 morphTarget2;","\tattribute vec3 morphTarget3;","\t#ifdef USE_MORPHNORMALS","\t\tattribute vec3 morphNormal0;","\t\tattribute vec3 morphNormal1;","\t\tattribute vec3 morphNormal2;","\t\tattribute vec3 morphNormal3;","\t#else","\t\tattribute vec3 morphTarget4;","\t\tattribute vec3 morphTarget5;","\t\tattribute vec3 morphTarget6;","\t\tattribute vec3 morphTarget7;","\t#endif","#endif","#ifdef USE_SKINNING","\tattribute vec4 skinIndex;","\tattribute vec4 skinWeight;","#endif","\n"].filter(Li).join("\n"),s=[w,Hi(n),"#define SHADER_NAME "+n.shaderName,M,n.alphaTest?"#define ALPHATEST "+n.alphaTest+(n.alphaTest%1?"":".0"):"","#define GAMMA_FACTOR "+b,n.useFog&&n.fog?"#define USE_FOG":"",n.useFog&&n.fogExp2?"#define FOG_EXP2":"",n.map?"#define USE_MAP":"",n.matcap?"#define USE_MATCAP":"",n.envMap?"#define USE_ENVMAP":"",n.envMap?"#define "+y:"",n.envMap?"#define "+x:"",n.envMap?"#define "+_:"",n.lightMap?"#define USE_LIGHTMAP":"",n.aoMap?"#define USE_AOMAP":"",n.emissiveMap?"#define USE_EMISSIVEMAP":"",n.bumpMap?"#define USE_BUMPMAP":"",n.normalMap?"#define USE_NORMALMAP":"",n.normalMap&&n.objectSpaceNormalMap?"#define OBJECTSPACE_NORMALMAP":"",n.normalMap&&n.tangentSpaceNormalMap?"#define TANGENTSPACE_NORMALMAP":"",n.clearcoatMap?"#define USE_CLEARCOATMAP":"",n.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",n.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",n.specularMap?"#define USE_SPECULARMAP":"",n.roughnessMap?"#define USE_ROUGHNESSMAP":"",n.metalnessMap?"#define USE_METALNESSMAP":"",n.alphaMap?"#define USE_ALPHAMAP":"",n.sheen?"#define USE_SHEEN":"",n.transmissionMap?"#define USE_TRANSMISSIONMAP":"",n.vertexTangents?"#define USE_TANGENT":"",n.vertexColors||n.instancingColor?"#define USE_COLOR":"",n.vertexUvs?"#define USE_UV":"",n.uvsVertexOnly?"#define UVS_VERTEX_ONLY":"",n.gradientMap?"#define USE_GRADIENTMAP":"",n.flatShading?"#define FLAT_SHADED":"",n.doubleSided?"#define DOUBLE_SIDED":"",n.flipSided?"#define FLIP_SIDED":"",n.shadowMapEnabled?"#define USE_SHADOWMAP":"",n.shadowMapEnabled?"#define "+g:"",n.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",n.physicallyCorrectLights?"#define PHYSICALLY_CORRECT_LIGHTS":"",n.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"",n.logarithmicDepthBuffer&&n.rendererExtensionFragDepth?"#define USE_LOGDEPTHBUF_EXT":"",(n.extensionShaderTextureLOD||n.envMap)&&n.rendererExtensionShaderTextureLod?"#define TEXTURE_LOD_EXT":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",0!==n.toneMapping?"#define TONE_MAPPING":"",0!==n.toneMapping?lr.tonemapping_pars_fragment:"",0!==n.toneMapping?Ai("toneMapping",n.toneMapping):"",n.dithering?"#define DITHERING":"",lr.encodings_pars_fragment,n.map?Ei("mapTexelToLinear",n.mapEncoding):"",n.matcap?Ei("matcapTexelToLinear",n.matcapEncoding):"",n.envMap?Ei("envMapTexelToLinear",n.envMapEncoding):"",n.emissiveMap?Ei("emissiveMapTexelToLinear",n.emissiveMapEncoding):"",n.lightMap?Ei("lightMapTexelToLinear",n.lightMapEncoding):"",(l="linearToOutputTexel",h=n.outputEncoding,d=Si(h),"vec4 "+l+"( vec4 value ) { return LinearTo"+d[0]+d[1]+"; }"),n.depthPacking?"#define DEPTH_PACKING "+n.depthPacking:"","\n"].filter(Li).join("\n")),m=Ci(m=Ri(m=Di(m),n),n),v=Ci(v=Ri(v=Di(v),n),n),m=Oi(m),v=Oi(v),n.isWebGL2&&!0!==n.isRawShaderMaterial&&(T="#version 300 es\n",i=["#define attribute in","#define varying out","#define texture2D texture"].join("\n")+"\n"+i,s=["#define varying in",n.glslVersion===it?"":"out highp vec4 pc_fragColor;",n.glslVersion===it?"":"#define gl_FragColor pc_fragColor","#define gl_FragDepthEXT gl_FragDepth","#define texture2D texture","#define textureCube texture","#define texture2DProj textureProj","#define texture2DLodEXT textureLod","#define texture2DProjLodEXT textureProjLod","#define textureCubeLodEXT textureLod","#define texture2DGradEXT textureGrad","#define texture2DProjGradEXT textureProjGrad","#define textureCubeGradEXT textureGrad"].join("\n")+"\n"+s);var E,A,L=T+s+v,R=wi(p,35633,T+i+m),C=wi(p,35632,L);if(p.attachShader(S,R),p.attachShader(S,C),void 0!==n.index0AttributeName?p.bindAttribLocation(S,0,n.index0AttributeName):!0===n.morphTargets&&p.bindAttribLocation(S,0,"position"),p.linkProgram(S),t.debug.checkShaderErrors){var P=p.getProgramInfoLog(S).trim(),D=p.getShaderInfoLog(R).trim(),I=p.getShaderInfoLog(C).trim(),N=!0,B=!0;if(!1===p.getProgramParameter(S,35714)){N=!1;var O=Ti(p,R,"vertex"),z=Ti(p,C,"fragment");console.error("THREE.WebGLProgram: shader error: ",p.getError(),"35715",p.getProgramParameter(S,35715),"gl.getProgramInfoLog",P,O,z)}else""!==P?console.warn("THREE.WebGLProgram: gl.getProgramInfoLog()",P):""!==D&&""!==I||(B=!1);B&&(this.diagnostics={runnable:N,programLog:P,vertexShader:{log:D,prefix:i},fragmentShader:{log:I,prefix:s}})}return p.deleteShader(R),p.deleteShader(C),this.getUniforms=function(){return void 0===E&&(E=new bi(p,S)),E},this.getAttributes=function(){return void 0===A&&(A=function(t,e){for(var n={},r=t.getProgramParameter(e,35721),i=0;i0,maxBones:E,useVertexTexture:h,morphTargets:i.morphTargets,morphNormals:i.morphNormals,maxMorphTargets:t.maxMorphTargets,maxMorphNormals:t.maxMorphNormals,numDirLights:o.directional.length,numPointLights:o.point.length,numSpotLights:o.spot.length,numRectAreaLights:o.rectArea.length,numHemiLights:o.hemi.length,numDirLightShadows:o.directionalShadowMap.length,numPointLightShadows:o.pointShadowMap.length,numSpotLightShadows:o.spotShadowMap.length,numClippingPlanes:a.numPlanes,numClipIntersection:a.numIntersection,dithering:i.dithering,shadowMapEnabled:t.shadowMap.enabled&&v.length>0,shadowMapType:t.shadowMap.type,toneMapping:i.toneMapped?t.toneMapping:0,physicallyCorrectLights:t.physicallyCorrectLights,premultipliedAlpha:i.premultipliedAlpha,alphaTest:i.alphaTest,doubleSided:2===i.side,flipSided:1===i.side,depthPacking:void 0!==i.depthPacking&&i.depthPacking,index0AttributeName:i.index0AttributeName,extensionDerivatives:i.extensions&&i.extensions.derivatives,extensionFragDepth:i.extensions&&i.extensions.fragDepth,extensionDrawBuffers:i.extensions&&i.extensions.drawBuffers,extensionShaderTextureLOD:i.extensions&&i.extensions.shaderTextureLOD,rendererExtensionFragDepth:s||n.has("EXT_frag_depth"),rendererExtensionDrawBuffers:s||n.has("WEBGL_draw_buffers"),rendererExtensionShaderTextureLod:s||n.has("EXT_shader_texture_lod"),customProgramCacheKey:i.customProgramCacheKey()}},getProgramCacheKey:function(e){var n=[];if(e.shaderID?n.push(e.shaderID):(n.push(e.fragmentShader),n.push(e.vertexShader)),void 0!==e.defines)for(var r in e.defines)n.push(r),n.push(e.defines[r]);if(!1===e.isRawShaderMaterial){for(var i=0;i1&&r.sort(t||Vi),i.length>1&&i.sort(e||Wi)}}}function qi(t){var e=new WeakMap;return{get:function(n,r){var i;return!1===e.has(n)?(i=new ji(t),e.set(n,[i])):r>=e.get(n).length?(i=new ji(t),e.get(n).push(i)):i=e.get(n)[r],i},dispose:function(){e=new WeakMap}}}function Xi(){var t={};return{get:function(e){if(void 0!==t[e.id])return t[e.id];var n;switch(e.type){case"DirectionalLight":n={direction:new Rt,color:new Ke};break;case"SpotLight":n={position:new Rt,direction:new Rt,color:new Ke,distance:0,coneCos:0,penumbraCos:0,decay:0};break;case"PointLight":n={position:new Rt,color:new Ke,distance:0,decay:0};break;case"HemisphereLight":n={direction:new Rt,skyColor:new Ke,groundColor:new Ke};break;case"RectAreaLight":n={color:new Ke,position:new Rt,halfWidth:new Rt,halfHeight:new Rt}}return t[e.id]=n,n}}}var Yi=0;function Zi(t,e){return(e.castShadow?1:0)-(t.castShadow?1:0)}function Ji(t,e){for(var n,r=new Xi,i=(n={},{get:function(t){if(void 0!==n[t.id])return n[t.id];var e;switch(t.type){case"DirectionalLight":case"SpotLight":e={shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new yt};break;case"PointLight":e={shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new yt,shadowCameraNear:1,shadowCameraFar:1e3}}return n[t.id]=e,e}}),a={version:0,hash:{directionalLength:-1,pointLength:-1,spotLength:-1,rectAreaLength:-1,hemiLength:-1,numDirectionalShadows:-1,numPointShadows:-1,numSpotShadows:-1},ambient:[0,0,0],probe:[],directional:[],directionalShadow:[],directionalShadowMap:[],directionalShadowMatrix:[],spot:[],spotShadow:[],spotShadowMap:[],spotShadowMatrix:[],rectArea:[],rectAreaLTC1:null,rectAreaLTC2:null,point:[],pointShadow:[],pointShadowMap:[],pointShadowMatrix:[],hemi:[]},o=0;o<9;o++)a.probe.push(new Rt);var s=new Rt,l=new re,c=new re;return{setup:function(n){for(var o=0,s=0,l=0,c=0;c<9;c++)a.probe[c].set(0,0,0);var u=0,h=0,d=0,p=0,f=0,m=0,v=0,g=0;n.sort(Zi);for(var y=0,x=n.length;y0&&(e.isWebGL2||!0===t.has("OES_texture_float_linear")?(a.rectAreaLTC1=cr.LTC_FLOAT_1,a.rectAreaLTC2=cr.LTC_FLOAT_2):!0===t.has("OES_texture_half_float_linear")?(a.rectAreaLTC1=cr.LTC_HALF_1,a.rectAreaLTC2=cr.LTC_HALF_2):console.error("THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.")),a.ambient[0]=o,a.ambient[1]=s,a.ambient[2]=l;var z=a.hash;z.directionalLength===u&&z.pointLength===h&&z.spotLength===d&&z.rectAreaLength===p&&z.hemiLength===f&&z.numDirectionalShadows===m&&z.numPointShadows===v&&z.numSpotShadows===g||(a.directional.length=u,a.spot.length=d,a.rectArea.length=p,a.point.length=h,a.hemi.length=f,a.directionalShadow.length=m,a.directionalShadowMap.length=m,a.pointShadow.length=v,a.pointShadowMap.length=v,a.spotShadow.length=g,a.spotShadowMap.length=g,a.directionalShadowMatrix.length=m,a.pointShadowMatrix.length=v,a.spotShadowMatrix.length=g,z.directionalLength=u,z.pointLength=h,z.spotLength=d,z.rectAreaLength=p,z.hemiLength=f,z.numDirectionalShadows=m,z.numPointShadows=v,z.numSpotShadows=g,a.version=Yi++)},setupView:function(t,e){for(var n=0,r=0,i=0,o=0,u=0,h=e.matrixWorldInverse,d=0,p=t.length;d=n.get(r).length?(a=new Qi(t,e),n.get(r).push(a)):a=n.get(r)[i],a},dispose:function(){n=new WeakMap}}}var $i=function(t){function e(e){var n;return(n=t.call(this)||this).type="MeshDepthMaterial",n.depthPacking=3200,n.skinning=!1,n.morphTargets=!1,n.map=null,n.alphaMap=null,n.displacementMap=null,n.displacementScale=1,n.displacementBias=0,n.wireframe=!1,n.wireframeLinewidth=1,n.fog=!1,n.setValues(e),n}return lt(e,t),e.prototype.copy=function(e){return t.prototype.copy.call(this,e),this.depthPacking=e.depthPacking,this.skinning=e.skinning,this.morphTargets=e.morphTargets,this.map=e.map,this.alphaMap=e.alphaMap,this.displacementMap=e.displacementMap,this.displacementScale=e.displacementScale,this.displacementBias=e.displacementBias,this.wireframe=e.wireframe,this.wireframeLinewidth=e.wireframeLinewidth,this},e}(je);$i.prototype.isMeshDepthMaterial=!0;var ta=function(t){function e(e){var n;return(n=t.call(this)||this).type="MeshDistanceMaterial",n.referencePosition=new Rt,n.nearDistance=1,n.farDistance=1e3,n.skinning=!1,n.morphTargets=!1,n.map=null,n.alphaMap=null,n.displacementMap=null,n.displacementScale=1,n.displacementBias=0,n.fog=!1,n.setValues(e),n}return lt(e,t),e.prototype.copy=function(e){return t.prototype.copy.call(this,e),this.referencePosition.copy(e.referencePosition),this.nearDistance=e.nearDistance,this.farDistance=e.farDistance,this.skinning=e.skinning,this.morphTargets=e.morphTargets,this.map=e.map,this.alphaMap=e.alphaMap,this.displacementMap=e.displacementMap,this.displacementScale=e.displacementScale,this.displacementBias=e.displacementBias,this},e}(je);ta.prototype.isMeshDistanceMaterial=!0;function ea(t,e,n){var r=new ir,i=new yt,a=new yt,o=new Tt,s=[],l=[],c={},u={0:1,1:0,2:2},h=new Yn({defines:{SAMPLE_RATE:2/8,HALF_SAMPLE_RATE:1/8},uniforms:{shadow_pass:{value:null},resolution:{value:new yt},radius:{value:4}},vertexShader:"void main() {\n\tgl_Position = vec4( position, 1.0 );\n}",fragmentShader:"uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n\tfor ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean * HALF_SAMPLE_RATE;\n\tsquared_mean = squared_mean * HALF_SAMPLE_RATE;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"}),d=h.clone();d.defines.HORIZONTAL_PASS=1;var p=new Sn;p.setAttribute("position",new nn(new Float32Array([-1,-1,.5,3,-1,.5,-1,3,.5]),3));var m=new kn(p,h),v=this;function y(n,r){var i=e.update(m);h.uniforms.shadow_pass.value=n.map.texture,h.uniforms.resolution.value=n.mapSize,h.uniforms.radius.value=n.radius,t.setRenderTarget(n.mapPass),t.clear(),t.renderBufferDirect(r,null,i,h,m,null),d.uniforms.shadow_pass.value=n.mapPass.texture,d.uniforms.resolution.value=n.mapSize,d.uniforms.radius.value=n.radius,t.setRenderTarget(n.map),t.clear(),t.renderBufferDirect(r,null,i,d,m,null)}function x(t,e,n){var r=t<<0|e<<1|n<<2,i=s[r];return void 0===i&&(i=new $i({depthPacking:3201,morphTargets:t,skinning:e}),s[r]=i),i}function _(t,e,n){var r=t<<0|e<<1|n<<2,i=l[r];return void 0===i&&(i=new ta({morphTargets:t,skinning:e}),l[r]=i),i}function b(e,n,r,i,a,o,s){var l=null,h=x,d=e.customDepthMaterial;if(!0===i.isPointLight&&(h=_,d=e.customDistanceMaterial),void 0===d){var p=!1;!0===r.morphTargets&&(p=n.morphAttributes&&n.morphAttributes.position&&n.morphAttributes.position.length>0);var f=!1;!0===e.isSkinnedMesh&&(!0===r.skinning?f=!0:console.warn("THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:",e)),l=h(p,f,!0===e.isInstancedMesh)}else l=d;if(t.localClippingEnabled&&!0===r.clipShadows&&0!==r.clippingPlanes.length){var m=l.uuid,v=r.uuid,g=c[m];void 0===g&&(g={},c[m]=g);var y=g[v];void 0===y&&(y=l.clone(),g[v]=y),l=y}return l.visible=r.visible,l.wireframe=r.wireframe,l.side=3===s?null!==r.shadowSide?r.shadowSide:r.side:null!==r.shadowSide?r.shadowSide:u[r.side],l.clipShadows=r.clipShadows,l.clippingPlanes=r.clippingPlanes,l.clipIntersection=r.clipIntersection,l.wireframeLinewidth=r.wireframeLinewidth,l.linewidth=r.linewidth,!0===i.isPointLight&&!0===l.isMeshDistanceMaterial&&(l.referencePosition.setFromMatrixPosition(i.matrixWorld),l.nearDistance=a,l.farDistance=o),l}function w(n,i,a,o,s){if(!1!==n.visible){if(n.layers.test(i.layers)&&(n.isMesh||n.isLine||n.isPoints)&&(n.castShadow||n.receiveShadow&&3===s)&&(!n.frustumCulled||r.intersectsObject(n))){n.modelViewMatrix.multiplyMatrices(a.matrixWorldInverse,n.matrixWorld);var l=e.update(n),c=n.material;if(Array.isArray(c))for(var u=l.groups,h=0,d=u.length;hn||i.y>n)&&(i.x>n&&(a.x=Math.floor(n/b.x),i.x=a.x*b.x,_.mapSize.x=a.x),i.y>n&&(a.y=Math.floor(n/b.y),i.y=a.y*b.y,_.mapSize.y=a.y)),null===_.map&&!_.isPointLightShadow&&3===this.type){var M={minFilter:g,magFilter:g,format:A};_.map=new Et(i.x,i.y,M),_.map.texture.name=x.name+".shadowMap",_.mapPass=new Et(i.x,i.y,M),_.camera.updateProjectionMatrix()}if(null===_.map){var S={minFilter:f,magFilter:f,format:A};_.map=new Et(i.x,i.y,S),_.map.texture.name=x.name+".shadowMap",_.camera.updateProjectionMatrix()}t.setRenderTarget(_.map),t.clear();for(var T=_.getViewportCount(),E=0;E=1):-1!==R.indexOf("OpenGL ES")&&(L=parseFloat(/^OpenGL ES (\d)/.exec(R)[1]),A=L>=2);var C=null,P={},D=new Tt,I=new Tt;function N(e,n,r){var i=new Uint8Array(4),a=t.createTexture();t.bindTexture(e,a),t.texParameteri(e,10241,9728),t.texParameteri(e,10240,9728);for(var o=0;or||t.height>r)&&(i=r/Math.max(t.width,t.height)),i<1||!0===e){if("undefined"!=typeof HTMLImageElement&&t instanceof HTMLImageElement||"undefined"!=typeof HTMLCanvasElement&&t instanceof HTMLCanvasElement||"undefined"!=typeof ImageBitmap&&t instanceof ImageBitmap){var a=e?gt.floorPowerOfTwo:Math.floor,o=a(i*t.width),s=a(i*t.height);void 0===c&&(c=P(o,s));var l=n?P(o,s):c;return l.width=o,l.height=s,l.getContext("2d").drawImage(t,0,0,o,s),console.warn("THREE.WebGLRenderer: Texture has been resized from ("+t.width+"x"+t.height+") to ("+o+"x"+s+")."),l}return"data"in t&&console.warn("THREE.WebGLRenderer: Image in DataTexture is too big ("+t.width+"x"+t.height+")."),t}return t}function I(t){return gt.isPowerOfTwo(t.width)&>.isPowerOfTwo(t.height)}function N(t,e){return t.generateMipmaps&&e&&t.minFilter!==f&&t.minFilter!==g}function B(e,n,i,a){t.generateMipmap(e),r.get(n).__maxMipLevel=Math.log2(Math.max(i,a))}function O(n,r,i){if(!1===u)return r;if(null!==n){if(void 0!==t[n])return t[n];console.warn("THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format '"+n+"'")}var a=r;return 6403===r&&(5126===i&&(a=33326),5131===i&&(a=33325),5121===i&&(a=33321)),6407===r&&(5126===i&&(a=34837),5131===i&&(a=34843),5121===i&&(a=32849)),6408===r&&(5126===i&&(a=34836),5131===i&&(a=34842),5121===i&&(a=32856)),33325!==a&&33326!==a&&34842!==a&&34836!==a||e.get("EXT_color_buffer_float"),a}function z(t){return t===f||t===m||t===v?9728:9729}function F(e){var n=e.target;n.removeEventListener("dispose",F),function(e){var n=r.get(e);if(void 0===n.__webglInit)return;t.deleteTexture(n.__webglTexture),r.remove(e)}(n),n.isVideoTexture&&_.delete(n),o.memory.textures--}function H(e){var n=e.target;n.removeEventListener("dispose",H),function(e){var n=e.texture,i=r.get(e),a=r.get(n);if(!e)return;void 0!==a.__webglTexture&&t.deleteTexture(a.__webglTexture);e.depthTexture&&e.depthTexture.dispose();if(e.isWebGLCubeRenderTarget)for(var o=0;o<6;o++)t.deleteFramebuffer(i.__webglFramebuffer[o]),i.__webglDepthbuffer&&t.deleteRenderbuffer(i.__webglDepthbuffer[o]);else t.deleteFramebuffer(i.__webglFramebuffer),i.__webglDepthbuffer&&t.deleteRenderbuffer(i.__webglDepthbuffer),i.__webglMultisampledFramebuffer&&t.deleteFramebuffer(i.__webglMultisampledFramebuffer),i.__webglColorRenderbuffer&&t.deleteRenderbuffer(i.__webglColorRenderbuffer),i.__webglDepthRenderbuffer&&t.deleteRenderbuffer(i.__webglDepthRenderbuffer);r.remove(n),r.remove(e)}(n),o.memory.textures--}var G=0;function U(t,e){var i=r.get(t);if(t.isVideoTexture&&function(t){var e=o.render.frame;_.get(t)!==e&&(_.set(t,e),t.update())}(t),t.version>0&&i.__version!==t.version){var a=t.image;if(void 0===a)console.warn("THREE.WebGLRenderer: Texture marked for update but image is undefined");else{if(!1!==a.complete)return void X(i,t,e);console.warn("THREE.WebGLRenderer: Texture marked for update but image is incomplete")}}n.activeTexture(33984+e),n.bindTexture(3553,i.__webglTexture)}function k(e,i){var o=r.get(e);e.version>0&&o.__version!==e.version?function(e,r,i){if(6!==r.image.length)return;q(e,r),n.activeTexture(33984+i),n.bindTexture(34067,e.__webglTexture),t.pixelStorei(37440,r.flipY),t.pixelStorei(37441,r.premultiplyAlpha),t.pixelStorei(3317,r.unpackAlignment),t.pixelStorei(37443,0);for(var o=r&&(r.isCompressedTexture||r.image[0].isCompressedTexture),s=r.image[0]&&r.image[0].isDataTexture,l=[],c=0;c<6;c++)l[c]=o||s?s?r.image[c].image:r.image[c]:D(r.image[c],!1,!0,p);var h,d=l[0],f=I(d)||u,m=a.convert(r.format),v=a.convert(r.type),g=O(r.internalFormat,m,v);if(j(34067,r,f),o){for(var y=0;y<6;y++){h=l[y].mipmaps;for(var x=0;x1||r.get(a).__currentAnisotropy)&&(t.texParameterf(n,s.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(a.anisotropy,i.getMaxAnisotropy())),r.get(a).__currentAnisotropy=a.anisotropy)}}function q(e,n){void 0===e.__webglInit&&(e.__webglInit=!0,n.addEventListener("dispose",F),e.__webglTexture=t.createTexture(),o.memory.textures++)}function X(e,r,i){var o=3553;r.isDataTexture2DArray&&(o=35866),r.isDataTexture3D&&(o=32879),q(e,r),n.activeTexture(33984+i),n.bindTexture(o,e.__webglTexture),t.pixelStorei(37440,r.flipY),t.pixelStorei(37441,r.premultiplyAlpha),t.pixelStorei(3317,r.unpackAlignment),t.pixelStorei(37443,0);var s,l=function(t){return!u&&(t.wrapS!==d||t.wrapT!==d||t.minFilter!==f&&t.minFilter!==g)}(r)&&!1===I(r.image),c=D(r.image,l,!1,y),h=I(c)||u,p=a.convert(r.format),m=a.convert(r.type),v=O(r.internalFormat,p,m);j(o,r,h);var x=r.mipmaps;if(r.isDepthTexture)v=6402,u?v=r.type===M?36012:r.type===w?33190:r.type===T?35056:33189:r.type===M&&console.error("WebGLRenderer: Floating point depth texture requires WebGL2."),r.format===L&&6402===v&&r.type!==b&&r.type!==w&&(console.warn("THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture."),r.type=b,m=a.convert(r.type)),r.format===R&&6402===v&&(v=34041,r.type!==T&&(console.warn("THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture."),r.type=T,m=a.convert(r.type))),n.texImage2D(3553,0,v,c.width,c.height,0,p,m,null);else if(r.isDataTexture)if(x.length>0&&h){for(var _=0,S=x.length;_0&&h){for(var z=0,F=x.length;z=h&&console.warn("THREE.WebGLTextures: Trying to use "+t+" texture units while this GPU supports only "+h),G+=1,t},this.resetTextureUnits=function(){G=0},this.setTexture2D=U,this.setTexture2DArray=function(t,e){var i=r.get(t);t.version>0&&i.__version!==t.version?X(i,t,e):(n.activeTexture(33984+e),n.bindTexture(35866,i.__webglTexture))},this.setTexture3D=function(t,e){var i=r.get(t);t.version>0&&i.__version!==t.version?X(i,t,e):(n.activeTexture(33984+e),n.bindTexture(32879,i.__webglTexture))},this.setTextureCube=k,this.setupRenderTarget=function(e){var i=e.texture,s=r.get(e),l=r.get(i);e.addEventListener("dispose",H),l.__webglTexture=t.createTexture(),o.memory.textures++;var c=!0===e.isWebGLCubeRenderTarget,h=!0===e.isWebGLMultisampleRenderTarget,d=i.isDataTexture3D||i.isDataTexture2DArray,p=I(e)||u;if(!u||i.format!==E||i.type!==M&&i.type!==S||(i.format=A,console.warn("THREE.WebGLRenderer: Rendering to textures with RGB format is not supported. Using RGBA format instead.")),c){s.__webglFramebuffer=[];for(var f=0;f<6;f++)s.__webglFramebuffer[f]=t.createFramebuffer()}else if(s.__webglFramebuffer=t.createFramebuffer(),h)if(u){s.__webglMultisampledFramebuffer=t.createFramebuffer(),s.__webglColorRenderbuffer=t.createRenderbuffer(),t.bindRenderbuffer(36161,s.__webglColorRenderbuffer);var m=a.convert(i.format),v=a.convert(i.type),g=O(i.internalFormat,m,v),y=Q(e);t.renderbufferStorageMultisample(36161,y,g,e.width,e.height),t.bindFramebuffer(36160,s.__webglMultisampledFramebuffer),t.framebufferRenderbuffer(36160,36064,36161,s.__webglColorRenderbuffer),t.bindRenderbuffer(36161,null),e.depthBuffer&&(s.__webglDepthRenderbuffer=t.createRenderbuffer(),Z(s.__webglDepthRenderbuffer,e,!0)),t.bindFramebuffer(36160,null)}else console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.");if(c){n.bindTexture(34067,l.__webglTexture),j(34067,i,p);for(var x=0;x<6;x++)Y(s.__webglFramebuffer[x],e,36064,34069+x);N(i,p)&&B(34067,i,e.width,e.height),n.bindTexture(34067,null)}else{var _=3553;if(d)if(u)_=i.isDataTexture3D?32879:35866;else console.warn("THREE.DataTexture3D and THREE.DataTexture2DArray only supported with WebGL2.");n.bindTexture(_,l.__webglTexture),j(_,i,p),Y(s.__webglFramebuffer,e,36064,_),N(i,p)&&B(3553,i,e.width,e.height),n.bindTexture(3553,null)}e.depthBuffer&&J(e)},this.updateRenderTargetMipmap=function(t){var e=t.texture;if(N(e,I(t)||u)){var i=t.isWebGLCubeRenderTarget?34067:3553,a=r.get(e).__webglTexture;n.bindTexture(i,a),B(i,e,t.width,t.height),n.bindTexture(i,null)}},this.updateMultisampleRenderTarget=function(e){if(e.isWebGLMultisampleRenderTarget)if(u){var n=r.get(e);t.bindFramebuffer(36008,n.__webglMultisampledFramebuffer),t.bindFramebuffer(36009,n.__webglFramebuffer);var i=e.width,a=e.height,o=16384;e.depthBuffer&&(o|=256),e.stencilBuffer&&(o|=1024),t.blitFramebuffer(0,0,i,a,0,0,i,a,o,9728),t.bindFramebuffer(36160,n.__webglMultisampledFramebuffer)}else console.warn("THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.")},this.safeSetTexture2D=function(t,e){t&&t.isWebGLRenderTarget&&(!1===K&&(console.warn("THREE.WebGLTextures.safeSetTexture2D: don't use render targets as textures. Use their .texture property instead."),K=!0),t=t.texture),U(t,e)},this.safeSetTextureCube=function(t,e){t&&t.isWebGLCubeRenderTarget&&(!1===$&&(console.warn("THREE.WebGLTextures.safeSetTextureCube: don't use cube render targets as textures. Use their .texture property instead."),$=!0),t=t.texture),k(t,e)}}function ia(t,e,n){var r=n.isWebGL2;return{convert:function(t){var n;if(t===_)return 5121;if(1017===t)return 32819;if(1018===t)return 32820;if(1019===t)return 33635;if(1010===t)return 5120;if(1011===t)return 5122;if(t===b)return 5123;if(1013===t)return 5124;if(t===w)return 5125;if(t===M)return 5126;if(t===S)return r?5131:null!==(n=e.get("OES_texture_half_float"))?n.HALF_FLOAT_OES:null;if(1021===t)return 6406;if(t===E)return 6407;if(t===A)return 6408;if(1024===t)return 6409;if(1025===t)return 6410;if(t===L)return 6402;if(t===R)return 34041;if(1028===t)return 6403;if(1029===t)return 36244;if(1030===t)return 33319;if(1031===t)return 33320;if(1032===t)return 36248;if(1033===t)return 36249;if(t===C||t===P||t===D||t===I){if(null===(n=e.get("WEBGL_compressed_texture_s3tc")))return null;if(t===C)return n.COMPRESSED_RGB_S3TC_DXT1_EXT;if(t===P)return n.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(t===D)return n.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(t===I)return n.COMPRESSED_RGBA_S3TC_DXT5_EXT}if(t===N||t===B||t===O||t===z){if(null===(n=e.get("WEBGL_compressed_texture_pvrtc")))return null;if(t===N)return n.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(t===B)return n.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(t===O)return n.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(t===z)return n.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}if(36196===t)return null!==(n=e.get("WEBGL_compressed_texture_etc1"))?n.COMPRESSED_RGB_ETC1_WEBGL:null;if((t===F||t===H)&&null!==(n=e.get("WEBGL_compressed_texture_etc"))){if(t===F)return n.COMPRESSED_RGB8_ETC2;if(t===H)return n.COMPRESSED_RGBA8_ETC2_EAC}return 37808===t||37809===t||37810===t||37811===t||37812===t||37813===t||37814===t||37815===t||37816===t||37817===t||37818===t||37819===t||37820===t||37821===t||37840===t||37841===t||37842===t||37843===t||37844===t||37845===t||37846===t||37847===t||37848===t||37849===t||37850===t||37851===t||37852===t||37853===t?null!==(n=e.get("WEBGL_compressed_texture_astc"))?t:null:36492===t?null!==(n=e.get("EXT_texture_compression_bptc"))?t:null:t===T?r?34042:null!==(n=e.get("WEBGL_depth_texture"))?n.UNSIGNED_INT_24_8_WEBGL:null:void 0}}}function aa(t){void 0===t&&(t=[]),Jn.call(this),this.cameras=t}aa.prototype=Object.assign(Object.create(Jn.prototype),{constructor:aa,isArrayCamera:!0});var oa=function(t){function e(){var e;return(e=t.call(this)||this).type="Group",e}return lt(e,t),e}(Le);function sa(){this._targetRay=null,this._grip=null,this._hand=null}function la(t,e){var n=this,r=null,i=1,a=null,o="local-floor",s=null,l=[],c=new Map,u=new Jn;u.layers.enable(1),u.viewport=new Tt;var h=new Jn;h.layers.enable(2),h.viewport=new Tt;var d=[u,h],p=new aa;p.layers.enable(1),p.layers.enable(2);var f=null,m=null;function v(t){var e=c.get(t.inputSource);e&&e.dispatchEvent({type:t.type,data:t.inputSource})}function g(){c.forEach((function(t,e){t.disconnect(e)})),c.clear(),f=null,m=null,t.setFramebuffer(null),t.setRenderTarget(t.getRenderTarget()),M.stop(),n.isPresenting=!1,n.dispatchEvent({type:"sessionend"})}function y(t){for(var e=r.inputSources,n=0;n0&&It(a,t,e),o.length>0&&It(o,t,e),!0===t.isScene&&t.onAfterRender(v,t,e),null!==w&&($.updateRenderTargetMipmap(w),$.updateMultisampleRenderTarget(w)),J.buffers.depth.setTest(!0),J.buffers.depth.setMask(!0),J.buffers.color.setMask(!0),J.setPolygonOffset(!1),m.pop(),p=m.length>0?m[m.length-1]:null,f.pop(),d=f.length>0?f[f.length-1]:null}}else console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.")},this.setFramebuffer=function(t){y!==t&&null===w&&mt.bindFramebuffer(36160,t),y=t},this.getActiveCubeFace=function(){return x},this.getActiveMipmapLevel=function(){return b},this.getRenderTarget=function(){return w},this.setRenderTarget=function(t,e,n){void 0===e&&(e=0),void 0===n&&(n=0),w=t,x=e,b=n,t&&void 0===K.get(t).__webglFramebuffer&&$.setupRenderTarget(t);var r=y,i=!1,a=!1;if(t){var o=t.texture;(o.isDataTexture3D||o.isDataTexture2DArray)&&(a=!0);var s=K.get(t).__webglFramebuffer;t.isWebGLCubeRenderTarget?(r=s[e],i=!0):r=t.isWebGLMultisampleRenderTarget?K.get(t).__webglMultisampledFramebuffer:s,R.copy(t.viewport),C.copy(t.scissor),P=t.scissorTest}else R.copy(z).multiplyScalar(N).floor(),C.copy(F).multiplyScalar(N).floor(),P=H;if(T!==r&&(mt.bindFramebuffer(36160,r),T=r),J.viewport(R),J.scissor(C),J.setScissorTest(P),i){var l=K.get(t.texture);mt.framebufferTexture2D(36160,36064,34069+e,l.__webglTexture,n)}else if(a){var c=K.get(t.texture),u=e||0;mt.framebufferTextureLayer(36160,36064,c.__webglTexture,n||0,u)}},this.readRenderTargetPixels=function(t,e,n,r,i,a,o){if(t&&t.isWebGLRenderTarget){var s=K.get(t).__webglFramebuffer;if(t.isWebGLCubeRenderTarget&&void 0!==o&&(s=s[o]),s){var l=!1;s!==T&&(mt.bindFramebuffer(36160,s),l=!0);try{var c=t.texture,u=c.format,h=c.type;if(u!==A&&pt.convert(u)!==mt.getParameter(35739))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");var d=h===S&&(X.has("EXT_color_buffer_half_float")||Z.isWebGL2&&X.has("EXT_color_buffer_float"));if(!(h===_||pt.convert(h)===mt.getParameter(35738)||h===M&&(Z.isWebGL2||X.has("OES_texture_float")||X.has("WEBGL_color_buffer_float"))||d))return void console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");36053===mt.checkFramebufferStatus(36160)?e>=0&&e<=t.width-r&&n>=0&&n<=t.height-i&&mt.readPixels(e,n,r,i,pt.convert(u),pt.convert(h),a):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.")}finally{l&&mt.bindFramebuffer(36160,T)}}}else console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.")},this.copyFramebufferToTexture=function(t,e,n){void 0===n&&(n=0);var r=Math.pow(2,-n),i=Math.floor(e.image.width*r),a=Math.floor(e.image.height*r),o=pt.convert(e.format);$.setTexture2D(e,0),mt.copyTexImage2D(3553,n,o,t.x,t.y,i,a,0),J.unbindTexture()},this.copyTextureToTexture=function(t,e,n,r){void 0===r&&(r=0);var i=e.image.width,a=e.image.height,o=pt.convert(n.format),s=pt.convert(n.type);$.setTexture2D(n,0),mt.pixelStorei(37440,n.flipY),mt.pixelStorei(37441,n.premultiplyAlpha),mt.pixelStorei(3317,n.unpackAlignment),e.isDataTexture?mt.texSubImage2D(3553,r,t.x,t.y,i,a,o,s,e.image.data):e.isCompressedTexture?mt.compressedTexSubImage2D(3553,r,t.x,t.y,e.mipmaps[0].width,e.mipmaps[0].height,o,e.mipmaps[0].data):mt.texSubImage2D(3553,r,t.x,t.y,o,s,e.image),0===r&&n.generateMipmaps&&mt.generateMipmap(3553),J.unbindTexture()},this.copyTextureToTexture3D=function(t,e,n,r,i){if(void 0===i&&(i=0),v.isWebGL1Renderer)console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.");else{var a,o=n.image,s=o.width,l=o.height,c=o.data,u=pt.convert(r.format),h=pt.convert(r.type);if(r.isDataTexture3D)$.setTexture3D(r,0),a=32879;else{if(!r.isDataTexture2DArray)return void console.warn("THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.");$.setTexture2DArray(r,0),a=35866}mt.pixelStorei(37440,r.flipY),mt.pixelStorei(37441,r.premultiplyAlpha),mt.pixelStorei(3317,r.unpackAlignment);var d=mt.getParameter(3314),p=mt.getParameter(32878),f=mt.getParameter(3316),m=mt.getParameter(3315),g=mt.getParameter(32877);mt.pixelStorei(3314,s),mt.pixelStorei(32878,l),mt.pixelStorei(3316,t.min.x),mt.pixelStorei(3315,t.min.y),mt.pixelStorei(32877,t.min.z),mt.texSubImage3D(a,i,e.x,e.y,e.z,t.max.x-t.min.x+1,t.max.y-t.min.y+1,t.max.z-t.min.z+1,u,h,c),mt.pixelStorei(3314,d),mt.pixelStorei(32878,p),mt.pixelStorei(3316,f),mt.pixelStorei(3315,m),mt.pixelStorei(32877,g),0===i&&r.generateMipmaps&&mt.generateMipmap(a),J.unbindTexture()}},this.initTexture=function(t){$.setTexture2D(t,0),J.unbindTexture()},this.resetState=function(){J.reset(),ft.reset()},"undefined"!=typeof __THREE_DEVTOOLS__&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}oa.prototype.isGroup=!0,Object.assign(sa.prototype,{constructor:sa,getHandSpace:function(){return null===this._hand&&(this._hand=new oa,this._hand.matrixAutoUpdate=!1,this._hand.visible=!1,this._hand.joints={},this._hand.inputState={pinching:!1}),this._hand},getTargetRaySpace:function(){return null===this._targetRay&&(this._targetRay=new oa,this._targetRay.matrixAutoUpdate=!1,this._targetRay.visible=!1),this._targetRay},getGripSpace:function(){return null===this._grip&&(this._grip=new oa,this._grip.matrixAutoUpdate=!1,this._grip.visible=!1),this._grip},dispatchEvent:function(t){return null!==this._targetRay&&this._targetRay.dispatchEvent(t),null!==this._grip&&this._grip.dispatchEvent(t),null!==this._hand&&this._hand.dispatchEvent(t),this},disconnect:function(t){return this.dispatchEvent({type:"disconnected",data:t}),null!==this._targetRay&&(this._targetRay.visible=!1),null!==this._grip&&(this._grip.visible=!1),null!==this._hand&&(this._hand.visible=!1),this},update:function(t,e,n){var r=null,i=null,a=null,o=this._targetRay,s=this._grip,l=this._hand;if(t&&"visible-blurred"!==e.session.visibilityState)if(l&&t.hand){a=!0;for(var c,u=dt(t.hand.values());!(c=u()).done;){var h=c.value,d=e.getJointPose(h,n);if(void 0===l.joints[h.jointName]){var p=new oa;p.matrixAutoUpdate=!1,p.visible=!1,l.joints[h.jointName]=p,l.add(p)}var f=l.joints[h.jointName];null!==d&&(f.matrix.fromArray(d.transform.matrix),f.matrix.decompose(f.position,f.rotation,f.scale),f.jointRadius=d.radius),f.visible=null!==d}var m=l.joints["index-finger-tip"],v=l.joints["thumb-tip"],g=m.position.distanceTo(v.position);l.inputState.pinching&&g>.025?(l.inputState.pinching=!1,this.dispatchEvent({type:"pinchend",handedness:t.handedness,target:this})):!l.inputState.pinching&&g<=.015&&(l.inputState.pinching=!0,this.dispatchEvent({type:"pinchstart",handedness:t.handedness,target:this}))}else null!==o&&null!==(r=e.getPose(t.targetRaySpace,n))&&(o.matrix.fromArray(r.transform.matrix),o.matrix.decompose(o.position,o.rotation,o.scale)),null!==s&&t.gripSpace&&null!==(i=e.getPose(t.gripSpace,n))&&(s.matrix.fromArray(i.transform.matrix),s.matrix.decompose(s.position,s.rotation,s.scale));return null!==o&&(o.visible=null!==r),null!==s&&(s.visible=null!==i),null!==l&&(l.visible=null!==a),this}}),Object.assign(la.prototype,pt.prototype);var ha=function(t){function e(){return t.apply(this,arguments)||this}return lt(e,t),e}(ua);ha.prototype.isWebGL1Renderer=!0;var da=function(){function t(t,e){this.name="",this.color=new Ke(t),this.density=void 0!==e?e:25e-5}var e=t.prototype;return e.clone=function(){return new t(this.color,this.density)},e.toJSON=function(){return{type:"FogExp2",color:this.color.getHex(),density:this.density}},t}();da.prototype.isFogExp2=!0;var pa=function(){function t(t,e,n){this.name="",this.color=new Ke(t),this.near=void 0!==e?e:1,this.far=void 0!==n?n:1e3}var e=t.prototype;return e.clone=function(){return new t(this.color,this.near,this.far)},e.toJSON=function(){return{type:"Fog",color:this.color.getHex(),near:this.near,far:this.far}},t}();pa.prototype.isFog=!0;var fa=function(t){function e(){var e;return(e=t.call(this)||this).type="Scene",e.background=null,e.environment=null,e.fog=null,e.overrideMaterial=null,e.autoUpdate=!0,"undefined"!=typeof __THREE_DEVTOOLS__&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:ut(e)})),e}lt(e,t);var n=e.prototype;return n.copy=function(e,n){return t.prototype.copy.call(this,e,n),null!==e.background&&(this.background=e.background.clone()),null!==e.environment&&(this.environment=e.environment.clone()),null!==e.fog&&(this.fog=e.fog.clone()),null!==e.overrideMaterial&&(this.overrideMaterial=e.overrideMaterial.clone()),this.autoUpdate=e.autoUpdate,this.matrixAutoUpdate=e.matrixAutoUpdate,this},n.toJSON=function(e){var n=t.prototype.toJSON.call(this,e);return null!==this.background&&(n.object.background=this.background.toJSON(e)),null!==this.environment&&(n.object.environment=this.environment.toJSON(e)),null!==this.fog&&(n.object.fog=this.fog.toJSON()),n},e}(Le);function ma(t,e){this.array=t,this.stride=e,this.count=void 0!==t?t.length/e:0,this.usage=nt,this.updateRange={offset:0,count:-1},this.version=0,this.uuid=gt.generateUUID()}fa.prototype.isScene=!0,Object.defineProperty(ma.prototype,"needsUpdate",{set:function(t){!0===t&&this.version++}}),Object.assign(ma.prototype,{isInterleavedBuffer:!0,onUploadCallback:function(){},setUsage:function(t){return this.usage=t,this},copy:function(t){return this.array=new t.array.constructor(t.array),this.count=t.count,this.stride=t.stride,this.usage=t.usage,this},copyAt:function(t,e,n){t*=this.stride,n*=e.stride;for(var r=0,i=this.stride;rt.far||e.push({distance:s,point:_a.clone(),uv:Ve.getUV(_a,Ea,Aa,La,Ra,Ca,Pa,new yt),face:null,object:this})}},n.copy=function(e){return t.prototype.copy.call(this,e),void 0!==e.center&&this.center.copy(e.center),this.material=e.material,this},e}(Le);function Ia(t,e,n,r,i,a){Ma.subVectors(t,n).addScalar(.5).multiply(r),void 0!==i?(Sa.x=a*Ma.x-i*Ma.y,Sa.y=i*Ma.x+a*Ma.y):Sa.copy(Ma),t.copy(e),t.x+=Sa.x,t.y+=Sa.y,t.applyMatrix4(Ta)}Da.prototype.isSprite=!0;var Na=new Rt,Ba=new Rt,Oa=function(t){function e(){var e;return(e=t.call(this)||this)._currentLevel=0,e.type="LOD",Object.defineProperties(ut(e),{levels:{enumerable:!0,value:[]},isLOD:{value:!0}}),e.autoUpdate=!0,e}lt(e,t);var n=e.prototype;return n.copy=function(e){t.prototype.copy.call(this,e,!1);for(var n=e.levels,r=0,i=n.length;r0){var n,r;for(n=1,r=e.length;n0){Na.setFromMatrixPosition(this.matrixWorld);var n=t.ray.origin.distanceTo(Na);this.getObjectForDistance(n).raycast(t,e)}},n.update=function(t){var e=this.levels;if(e.length>1){Na.setFromMatrixPosition(t.matrixWorld),Ba.setFromMatrixPosition(this.matrixWorld);var n,r,i=Na.distanceTo(Ba)/t.zoom;for(e[0].object.visible=!0,n=1,r=e.length;n=e[n].distance;n++)e[n-1].object.visible=!1,e[n].object.visible=!0;for(this._currentLevel=n-1;no)){u.applyMatrix4(this.matrixWorld);var x=t.ray.origin.distanceTo(u);xt.far||e.push({distance:x,point:c.clone().applyMatrix4(this.matrixWorld),index:m,face:null,faceIndex:null,object:this})}}else for(var _=0,b=p.count-1;_o)){u.applyMatrix4(this.matrixWorld);var w=t.ray.origin.distanceTo(u);wt.far||e.push({distance:w,point:c.clone().applyMatrix4(this.matrixWorld),index:_,face:null,faceIndex:null,object:this})}}}else n.isGeometry&&console.error("THREE.Line.raycast() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.")}},updateMorphTargets:function(){var t=this.geometry;if(t.isBufferGeometry){var e=t.morphAttributes,n=Object.keys(e);if(n.length>0){var r=e[n[0]];if(void 0!==r){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(var i=0,a=r.length;i0&&console.error("THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.")}}});var ao=new Rt,oo=new Rt;function so(t,e){io.call(this,t,e),this.type="LineSegments"}so.prototype=Object.assign(Object.create(io.prototype),{constructor:so,isLineSegments:!0,computeLineDistances:function(){var t=this.geometry;if(t.isBufferGeometry)if(null===t.index){for(var e=t.attributes.position,n=[],r=0,i=e.count;ri.far)return;a.push({distance:c,distanceToRay:Math.sqrt(s),point:l,index:e,face:null,object:o})}}mo.prototype=Object.assign(Object.create(Le.prototype),{constructor:mo,isPoints:!0,copy:function(t){return Le.prototype.copy.call(this,t),this.material=t.material,this.geometry=t.geometry,this},raycast:function(t,e){var n=this.geometry,r=this.matrixWorld,i=t.params.Points.threshold;if(null===n.boundingSphere&&n.computeBoundingSphere(),po.copy(n.boundingSphere),po.applyMatrix4(r),po.radius+=i,!1!==t.ray.intersectsSphere(po)){uo.copy(r).invert(),ho.copy(t.ray).applyMatrix4(uo);var a=i/((this.scale.x+this.scale.y+this.scale.z)/3),o=a*a;if(n.isBufferGeometry){var s=n.index,l=n.attributes.position;if(null!==s)for(var c=s.array,u=0,h=c.length;u0){var r=e[n[0]];if(void 0!==r){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(var i=0,a=r.length;i0&&console.error("THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.")}}});var go=function(t){function e(e,n,r,i,a,o,s,l,c){var u;(u=t.call(this,e,n,r,i,a,o,s,l,c)||this).format=void 0!==s?s:E,u.minFilter=void 0!==o?o:g,u.magFilter=void 0!==a?a:g,u.generateMipmaps=!1;var h=ut(u);return"requestVideoFrameCallback"in e&&e.requestVideoFrameCallback((function t(){h.needsUpdate=!0,e.requestVideoFrameCallback(t)})),u}lt(e,t);var n=e.prototype;return n.clone=function(){return new this.constructor(this.image).copy(this)},n.update=function(){var t=this.image;!1==="requestVideoFrameCallback"in t&&t.readyState>=t.HAVE_CURRENT_DATA&&(this.needsUpdate=!0)},e}(Mt);go.prototype.isVideoTexture=!0;var yo=function(t){function e(e,n,r,i,a,o,s,l,c,u,h,d){var p;return(p=t.call(this,null,o,s,l,c,u,i,a,h,d)||this).image={width:n,height:r},p.mipmaps=e,p.flipY=!1,p.generateMipmaps=!1,p}return lt(e,t),e}(Mt);yo.prototype.isCompressedTexture=!0;var xo=function(t){function e(e,n,r,i,a,o,s,l,c){var u;return(u=t.call(this,e,n,r,i,a,o,s,l,c)||this).needsUpdate=!0,u}return lt(e,t),e}(Mt);xo.prototype.isCanvasTexture=!0;var _o=function(t){function e(e,n,r,i,a,o,s,l,c,u){var h;if((u=void 0!==u?u:L)!==L&&u!==R)throw new Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat");return void 0===r&&u===L&&(r=b),void 0===r&&u===R&&(r=T),(h=t.call(this,null,i,a,o,s,l,u,r,c)||this).image={width:e,height:n},h.magFilter=void 0!==s?s:f,h.minFilter=void 0!==l?l:f,h.flipY=!1,h.generateMipmaps=!1,h}return lt(e,t),e}(Mt);_o.prototype.isDepthTexture=!0;var bo=function(t){function e(e,n,r,i){var a;void 0===e&&(e=1),void 0===n&&(n=8),void 0===r&&(r=0),void 0===i&&(i=2*Math.PI),(a=t.call(this)||this).type="CircleGeometry",a.parameters={radius:e,segments:n,thetaStart:r,thetaLength:i},n=Math.max(3,n);var o=[],s=[],l=[],c=[],u=new Rt,h=new yt;s.push(0,0,0),l.push(0,0,1),c.push(.5,.5);for(var d=0,p=3;d<=n;d++,p+=3){var f=r+d/n*i;u.x=e*Math.cos(f),u.y=e*Math.sin(f),s.push(u.x,u.y,u.z),l.push(0,0,1),h.x=(s[p]/e+1)/2,h.y=(s[p+1]/e+1)/2,c.push(h.x,h.y)}for(var m=1;m<=n;m++)o.push(m,m+1,0);return a.setIndex(o),a.setAttribute("position",new dn(s,3)),a.setAttribute("normal",new dn(l,3)),a.setAttribute("uv",new dn(c,2)),a}return lt(e,t),e}(Sn),wo=function(t){function e(e,n,r,i,a,o,s,l){var c;void 0===e&&(e=1),void 0===n&&(n=1),void 0===r&&(r=1),void 0===i&&(i=8),void 0===a&&(a=1),void 0===o&&(o=!1),void 0===s&&(s=0),void 0===l&&(l=2*Math.PI),(c=t.call(this)||this).type="CylinderGeometry",c.parameters={radiusTop:e,radiusBottom:n,height:r,radialSegments:i,heightSegments:a,openEnded:o,thetaStart:s,thetaLength:l};var u=ut(c);i=Math.floor(i),a=Math.floor(a);var h=[],d=[],p=[],f=[],m=0,v=[],g=r/2,y=0;function x(t){for(var r=m,a=new yt,o=new Rt,c=0,v=!0===t?e:n,x=!0===t?1:-1,_=1;_<=i;_++)d.push(0,g*x,0),p.push(0,x,0),f.push(.5,.5),m++;for(var b=m,w=0;w<=i;w++){var M=w/i*l+s,S=Math.cos(M),T=Math.sin(M);o.x=v*T,o.y=g*x,o.z=v*S,d.push(o.x,o.y,o.z),p.push(0,x,0),a.x=.5*S+.5,a.y=.5*T*x+.5,f.push(a.x,a.y),m++}for(var E=0;E0&&x(!0),n>0&&x(!1)),c.setIndex(h),c.setAttribute("position",new dn(d,3)),c.setAttribute("normal",new dn(p,3)),c.setAttribute("uv",new dn(f,2)),c}return lt(e,t),e}(Sn),Mo=function(t){function e(e,n,r,i,a,o,s){var l;return void 0===e&&(e=1),void 0===n&&(n=1),void 0===r&&(r=8),void 0===i&&(i=1),void 0===a&&(a=!1),void 0===o&&(o=0),void 0===s&&(s=2*Math.PI),(l=t.call(this,0,e,n,r,i,a,o,s)||this).type="ConeGeometry",l.parameters={radius:e,height:n,radialSegments:r,heightSegments:i,openEnded:a,thetaStart:o,thetaLength:s},l}return lt(e,t),e}(wo),So=function(t){function e(e,n,r,i){var a;void 0===r&&(r=1),void 0===i&&(i=0),(a=t.call(this)||this).type="PolyhedronGeometry",a.parameters={vertices:e,indices:n,radius:r,detail:i};var o=[],s=[];function l(t,e,n,r){for(var i=r+1,a=[],o=0;o<=i;o++){a[o]=[];for(var s=t.clone().lerp(n,o/i),l=e.clone().lerp(n,o/i),u=i-o,h=0;h<=u;h++)a[o][h]=0===h&&o===i?s:s.clone().lerp(l,h/u)}for(var d=0;d.9&&a<.1&&(e<.2&&(s[t+0]+=1),n<.2&&(s[t+2]+=1),r<.2&&(s[t+4]+=1))}}()}(),a.setAttribute("position",new dn(o,3)),a.setAttribute("normal",new dn(o.slice(),3)),a.setAttribute("uv",new dn(s,2)),0===i?a.computeVertexNormals():a.normalizeNormals(),a}return lt(e,t),e}(Sn),To=function(t){function e(e,n){var r;void 0===e&&(e=1),void 0===n&&(n=0);var i=(1+Math.sqrt(5))/2,a=1/i,o=[-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,1,1,0,-a,-i,0,-a,i,0,a,-i,0,a,i,-a,-i,0,-a,i,0,a,-i,0,a,i,0,-i,0,-a,i,0,-a,-i,0,a,i,0,a];return(r=t.call(this,o,[3,11,7,3,7,15,3,15,13,7,19,17,7,17,6,7,6,15,17,4,8,17,8,10,17,10,6,8,0,16,8,16,2,8,2,10,0,12,1,0,1,18,0,18,16,6,10,2,6,2,13,6,13,15,2,16,18,2,18,3,2,3,13,18,1,9,18,9,11,18,11,3,4,14,12,4,12,0,4,0,8,11,9,5,11,5,19,11,19,7,19,5,14,19,14,4,19,4,17,1,12,14,1,14,5,1,5,9],e,n)||this).type="DodecahedronGeometry",r.parameters={radius:e,detail:n},r}return lt(e,t),e}(So),Eo=new Rt,Ao=new Rt,Lo=new Rt,Ro=new Ve,Co=function(t){function e(e,n){var r;if((r=t.call(this)||this).type="EdgesGeometry",r.parameters={thresholdAngle:n},n=void 0!==n?n:1,!0===e.isGeometry)return console.error("THREE.EdgesGeometry no longer supports THREE.Geometry. Use THREE.BufferGeometry instead."),ut(r);for(var i=Math.pow(10,4),a=Math.cos(gt.DEG2RAD*n),o=e.getIndex(),s=e.getAttribute("position"),l=o?o.count:s.count,c=[0,0,0],u=["a","b","c"],h=new Array(3),d={},p=[],f=0;f80*n){r=a=t[0],i=o=t[1];for(var f=n;fa&&(a=s),l>o&&(o=l);c=0!==(c=Math.max(a-r,o-i))?1/c:0}return No(d,p,n,r,i,c),p};function Do(t,e,n,r,i){var a,o;if(i===function(t,e,n,r){for(var i=0,a=e,o=n-r;a0)for(a=e;a=e;a-=r)o=$o(a,t[a],t[a+1],o);return o&&Xo(o,o.next)&&(ts(o),o=o.next),o}function Io(t,e){if(!t)return t;e||(e=t);var n,r=t;do{if(n=!1,r.steiner||!Xo(r,r.next)&&0!==qo(r.prev,r,r.next))r=r.next;else{if(ts(r),(r=e=r.prev)===r.next)break;n=!0}}while(n||r!==e);return e}function No(t,e,n,r,i,a,o){if(t){!o&&a&&function(t,e,n,r){var i=t;do{null===i.z&&(i.z=ko(i.x,i.y,e,n,r)),i.prevZ=i.prev,i.nextZ=i.next,i=i.next}while(i!==t);i.prevZ.nextZ=null,i.prevZ=null,function(t){var e,n,r,i,a,o,s,l,c=1;do{for(n=t,t=null,a=null,o=0;n;){for(o++,r=n,s=0,e=0;e0||l>0&&r;)0!==s&&(0===l||!r||n.z<=r.z)?(i=n,n=n.nextZ,s--):(i=r,r=r.nextZ,l--),a?a.nextZ=i:t=i,i.prevZ=a,a=i;n=r}a.nextZ=null,c*=2}while(o>1)}(i)}(t,r,i,a);for(var s,l,c=t;t.prev!==t.next;)if(s=t.prev,l=t.next,a?Oo(t,r,i,a):Bo(t))e.push(s.i/n),e.push(t.i/n),e.push(l.i/n),ts(t),t=l.next,c=l.next;else if((t=l)===c){o?1===o?No(t=zo(Io(t),e,n),e,n,r,i,a,2):2===o&&Fo(t,e,n,r,i,a):No(Io(t),e,n,r,i,a,1);break}}}function Bo(t){var e=t.prev,n=t,r=t.next;if(qo(e,n,r)>=0)return!1;for(var i=t.next.next;i!==t.prev;){if(Wo(e.x,e.y,n.x,n.y,r.x,r.y,i.x,i.y)&&qo(i.prev,i,i.next)>=0)return!1;i=i.next}return!0}function Oo(t,e,n,r){var i=t.prev,a=t,o=t.next;if(qo(i,a,o)>=0)return!1;for(var s=i.xa.x?i.x>o.x?i.x:o.x:a.x>o.x?a.x:o.x,u=i.y>a.y?i.y>o.y?i.y:o.y:a.y>o.y?a.y:o.y,h=ko(s,l,e,n,r),d=ko(c,u,e,n,r),p=t.prevZ,f=t.nextZ;p&&p.z>=h&&f&&f.z<=d;){if(p!==t.prev&&p!==t.next&&Wo(i.x,i.y,a.x,a.y,o.x,o.y,p.x,p.y)&&qo(p.prev,p,p.next)>=0)return!1;if(p=p.prevZ,f!==t.prev&&f!==t.next&&Wo(i.x,i.y,a.x,a.y,o.x,o.y,f.x,f.y)&&qo(f.prev,f,f.next)>=0)return!1;f=f.nextZ}for(;p&&p.z>=h;){if(p!==t.prev&&p!==t.next&&Wo(i.x,i.y,a.x,a.y,o.x,o.y,p.x,p.y)&&qo(p.prev,p,p.next)>=0)return!1;p=p.prevZ}for(;f&&f.z<=d;){if(f!==t.prev&&f!==t.next&&Wo(i.x,i.y,a.x,a.y,o.x,o.y,f.x,f.y)&&qo(f.prev,f,f.next)>=0)return!1;f=f.nextZ}return!0}function zo(t,e,n){var r=t;do{var i=r.prev,a=r.next.next;!Xo(i,a)&&Yo(i,r,r.next,a)&&Qo(i,a)&&Qo(a,i)&&(e.push(i.i/n),e.push(r.i/n),e.push(a.i/n),ts(r),ts(r.next),r=t=a),r=r.next}while(r!==t);return Io(r)}function Fo(t,e,n,r,i,a){var o=t;do{for(var s=o.next.next;s!==o.prev;){if(o.i!==s.i&&jo(o,s)){var l=Ko(o,s);return o=Io(o,o.next),l=Io(l,l.next),No(o,e,n,r,i,a),void No(l,e,n,r,i,a)}s=s.next}o=o.next}while(o!==t)}function Ho(t,e){return t.x-e.x}function Go(t,e){if(e=function(t,e){var n,r=e,i=t.x,a=t.y,o=-1/0;do{if(a<=r.y&&a>=r.next.y&&r.next.y!==r.y){var s=r.x+(a-r.y)*(r.next.x-r.x)/(r.next.y-r.y);if(s<=i&&s>o){if(o=s,s===i){if(a===r.y)return r;if(a===r.next.y)return r.next}n=r.x=r.x&&r.x>=u&&i!==r.x&&Wo(an.x||r.x===n.x&&Uo(n,r)))&&(n=r,d=l)),r=r.next}while(r!==c);return n}(t,e)){var n=Ko(e,t);Io(e,e.next),Io(n,n.next)}}function Uo(t,e){return qo(t.prev,t,e.prev)<0&&qo(e.next,t,t.next)<0}function ko(t,e,n,r,i){return(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=32767*(t-n)*i)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=32767*(e-r)*i)|e<<8))|e<<4))|e<<2))|e<<1))<<1}function Vo(t){var e=t,n=t;do{(e.x=0&&(t-o)*(r-s)-(n-o)*(e-s)>=0&&(n-o)*(a-s)-(i-o)*(r-s)>=0}function jo(t,e){return t.next.i!==e.i&&t.prev.i!==e.i&&!function(t,e){var n=t;do{if(n.i!==t.i&&n.next.i!==t.i&&n.i!==e.i&&n.next.i!==e.i&&Yo(n,n.next,t,e))return!0;n=n.next}while(n!==t);return!1}(t,e)&&(Qo(t,e)&&Qo(e,t)&&function(t,e){var n=t,r=!1,i=(t.x+e.x)/2,a=(t.y+e.y)/2;do{n.y>a!=n.next.y>a&&n.next.y!==n.y&&i<(n.next.x-n.x)*(a-n.y)/(n.next.y-n.y)+n.x&&(r=!r),n=n.next}while(n!==t);return r}(t,e)&&(qo(t.prev,t,e.prev)||qo(t,e.prev,e))||Xo(t,e)&&qo(t.prev,t,t.next)>0&&qo(e.prev,e,e.next)>0)}function qo(t,e,n){return(e.y-t.y)*(n.x-e.x)-(e.x-t.x)*(n.y-e.y)}function Xo(t,e){return t.x===e.x&&t.y===e.y}function Yo(t,e,n,r){var i=Jo(qo(t,e,n)),a=Jo(qo(t,e,r)),o=Jo(qo(n,r,t)),s=Jo(qo(n,r,e));return i!==a&&o!==s||(!(0!==i||!Zo(t,n,e))||(!(0!==a||!Zo(t,r,e))||(!(0!==o||!Zo(n,t,r))||!(0!==s||!Zo(n,e,r)))))}function Zo(t,e,n){return e.x<=Math.max(t.x,n.x)&&e.x>=Math.min(t.x,n.x)&&e.y<=Math.max(t.y,n.y)&&e.y>=Math.min(t.y,n.y)}function Jo(t){return t>0?1:t<0?-1:0}function Qo(t,e){return qo(t.prev,t,t.next)<0?qo(t,e,t.next)>=0&&qo(t,t.prev,e)>=0:qo(t,e,t.prev)<0||qo(t,t.next,e)<0}function Ko(t,e){var n=new es(t.i,t.x,t.y),r=new es(e.i,e.x,e.y),i=t.next,a=e.prev;return t.next=e,e.prev=t,n.next=i,i.prev=n,r.next=n,n.prev=r,a.next=r,r.prev=a,r}function $o(t,e,n,r){var i=new es(t,e,n);return r?(i.next=r.next,i.prev=r,r.next.prev=i,r.next=i):(i.prev=i,i.next=i),i}function ts(t){t.next.prev=t.prev,t.prev.next=t.next,t.prevZ&&(t.prevZ.nextZ=t.nextZ),t.nextZ&&(t.nextZ.prevZ=t.prevZ)}function es(t,e,n){this.i=t,this.x=e,this.y=n,this.prev=null,this.next=null,this.z=null,this.prevZ=null,this.nextZ=null,this.steiner=!1}var ns={area:function(t){for(var e=t.length,n=0,r=e-1,i=0;i2&&t[e-1].equals(t[0])&&t.pop()}function is(t,e){for(var n=0;nNumber.EPSILON){var d=Math.sqrt(u),p=Math.sqrt(l*l+c*c),f=e.x-s/d,m=e.y+o/d,v=((n.x-c/p-f)*c-(n.y+l/p-m)*l)/(o*c-s*l),g=(r=f+o*v-t.x)*r+(i=m+s*v-t.y)*i;if(g<=2)return new yt(r,i);a=Math.sqrt(g/2)}else{var y=!1;o>Number.EPSILON?l>Number.EPSILON&&(y=!0):o<-Number.EPSILON?l<-Number.EPSILON&&(y=!0):Math.sign(s)===Math.sign(c)&&(y=!0),y?(r=-s,i=o,a=Math.sqrt(u)):(r=o,i=s,a=Math.sqrt(u/2))}return new yt(r/a,i/a)}for(var z=[],F=0,H=R.length,G=H-1,U=F+1;F=0;gt--){for(var xt=gt/p,_t=u*Math.cos(xt*Math.PI/2),bt=h*Math.sin(xt*Math.PI/2)+d,wt=0,Mt=R.length;wt=0;){var r=n,i=n-1;i<0&&(i=t.length-1);for(var a=0,o=s+2*p;a=0?(t(g-s,m,u),h.subVectors(c,u)):(t(g+s,m,u),h.subVectors(u,c)),m-s>=0?(t(g,m-s,u),d.subVectors(c,u)):(t(g,m+s,u),d.subVectors(u,c)),l.crossVectors(h,d).normalize(),a.push(l.x,l.y,l.z),o.push(g,m)}for(var y=0;y0)&&f.push(E,A,R),(S!==r-1||c=r)){u.push(l.times[d]);for(var f=0;fa.tracks[v].times[0]&&(m=a.tracks[v].times[0]);for(var g=0;g=r.times[h]){var m=h*l+s,v=m+l-s;d=Ds.arraySlice(r.values,m,v)}else{var g=r.createInterpolant(),y=s,x=l-s;g.evaluate(a),d=Ds.arraySlice(g.resultBuffer,y,x)}"quaternion"===i&&(new Lt).fromArray(d).normalize().conjugate().toArray(d);for(var _=o.times.length,b=0;b<_;++b){var w=b*u+c;if("quaternion"===i)Lt.multiplyQuaternionsFlat(o.values,w,d,0,o.values,w);else for(var M=u-2*c,S=0;S=i)break t;var s=e[1];t=(i=e[--n-1]))break e}a=n,n=0}for(;n>>1;te;)--a;if(++a,0!==i||a!==r){i>=a&&(i=(a=Math.max(a,1))-1);var o=this.getValueSize();this.times=Ds.arraySlice(n,i,a),this.values=Ds.arraySlice(this.values,i*o,a*o)}return this},e.validate=function(){var t=!0,e=this.getValueSize();e-Math.floor(e)!=0&&(console.error("THREE.KeyframeTrack: Invalid value size in track.",this),t=!1);var n=this.times,r=this.values,i=n.length;0===i&&(console.error("THREE.KeyframeTrack: Track is empty.",this),t=!1);for(var a=null,o=0;o!==i;o++){var s=n[o];if("number"==typeof s&&isNaN(s)){console.error("THREE.KeyframeTrack: Time is not a valid number.",this,o,s),t=!1;break}if(null!==a&&a>s){console.error("THREE.KeyframeTrack: Out of order keys.",this,o,s,a),t=!1;break}a=s}if(void 0!==r&&Ds.isTypedArray(r))for(var l=0,c=r.length;l!==c;++l){var u=r[l];if(isNaN(u)){console.error("THREE.KeyframeTrack: Value is not a valid number.",this,l,u),t=!1;break}}return t},e.optimize=function(){for(var t=Ds.arraySlice(this.times),e=Ds.arraySlice(this.values),n=this.getValueSize(),r=this.getInterpolation()===k,i=t.length-1,a=1,o=1;o0){t[a]=t[i];for(var g=i*n,y=a*n,x=0;x!==n;++x)e[y+x]=e[g+x];++a}return a!==t.length?(this.times=Ds.arraySlice(t,0,a),this.values=Ds.arraySlice(e,0,a*n)):(this.times=t,this.values=e),this},e.clone=function(){var t=Ds.arraySlice(this.times,0),e=Ds.arraySlice(this.values,0),n=new(0,this.constructor)(this.name,t,e);return n.createInterpolant=this.createInterpolant,n},t}();zs.prototype.TimeBufferType=Float32Array,zs.prototype.ValueBufferType=Float32Array,zs.prototype.DefaultInterpolation=U;var Fs=function(t){function e(){return t.apply(this,arguments)||this}return lt(e,t),e}(zs);Fs.prototype.ValueTypeName="bool",Fs.prototype.ValueBufferType=Array,Fs.prototype.DefaultInterpolation=G,Fs.prototype.InterpolantFactoryMethodLinear=void 0,Fs.prototype.InterpolantFactoryMethodSmooth=void 0;var Hs=function(t){function e(){return t.apply(this,arguments)||this}return lt(e,t),e}(zs);Hs.prototype.ValueTypeName="color";var Gs=function(t){function e(){return t.apply(this,arguments)||this}return lt(e,t),e}(zs);function Us(t,e,n,r){Is.call(this,t,e,n,r)}Gs.prototype.ValueTypeName="number",Us.prototype=Object.assign(Object.create(Is.prototype),{constructor:Us,interpolate_:function(t,e,n,r){for(var i=this.resultBuffer,a=this.sampleValues,o=this.valueSize,s=(n-e)/(r-e),l=t*o,c=l+o;l!==c;l+=4)Lt.slerpFlat(i,0,a,l-o,a,l,s);return i}});var ks=function(t){function e(){return t.apply(this,arguments)||this}return lt(e,t),e.prototype.InterpolantFactoryMethodLinear=function(t){return new Us(this.times,this.values,this.getValueSize(),t)},e}(zs);ks.prototype.ValueTypeName="quaternion",ks.prototype.DefaultInterpolation=U,ks.prototype.InterpolantFactoryMethodSmooth=void 0;var Vs=function(t){function e(){return t.apply(this,arguments)||this}return lt(e,t),e}(zs);Vs.prototype.ValueTypeName="string",Vs.prototype.ValueBufferType=Array,Vs.prototype.DefaultInterpolation=G,Vs.prototype.InterpolantFactoryMethodLinear=void 0,Vs.prototype.InterpolantFactoryMethodSmooth=void 0;var Ws=function(t){function e(){return t.apply(this,arguments)||this}return lt(e,t),e}(zs);Ws.prototype.ValueTypeName="vector";var js=function(){function t(t,e,n,r){void 0===e&&(e=-1),void 0===r&&(r=q),this.name=t,this.tracks=n,this.duration=e,this.blendMode=r,this.uuid=gt.generateUUID(),this.duration<0&&this.resetDuration()}t.parse=function(t){for(var e=[],n=t.tracks,r=1/(t.fps||1),i=0,a=n.length;i!==a;++i)e.push(qs(n[i]).scale(r));var o=new this(t.name,t.duration,e,t.blendMode);return o.uuid=t.uuid,o},t.toJSON=function(t){for(var e=[],n=t.tracks,r={name:t.name,duration:t.duration,tracks:e,uuid:t.uuid,blendMode:t.blendMode},i=0,a=n.length;i!==a;++i)e.push(zs.toJSON(n[i]));return r},t.CreateFromMorphTargetSequence=function(t,e,n,r){for(var i=e.length,a=[],o=0;o1){var c=l[1],u=r[c];u||(r[c]=u=[]),u.push(s)}}var h=[];for(var d in r)h.push(this.CreateFromMorphTargetSequence(d,r[d],e,n));return h},t.parseAnimation=function(t,e){if(!t)return console.error("THREE.AnimationClip: No animation in JSONLoader data."),null;for(var n=function(t,e,n,r,i){if(0!==n.length){var a=[],o=[];Ds.flattenJSON(n,a,o,r),0!==a.length&&i.push(new t(e,a,o))}},r=[],i=t.name||"default",a=t.fps||30,o=t.blendMode,s=t.length||-1,l=t.hierarchy||[],c=0;c0||0===t.search(/^data\:image\/jpeg/);i.format=r?E:A,i.needsUpdate=!0,void 0!==e&&e(i)}),n,r),i}}),Object.assign(al.prototype,{getPoint:function(){return console.warn("THREE.Curve: .getPoint() not implemented."),null},getPointAt:function(t,e){var n=this.getUtoTmapping(t);return this.getPoint(n,e)},getPoints:function(t){void 0===t&&(t=5);for(var e=[],n=0;n<=t;n++)e.push(this.getPoint(n/t));return e},getSpacedPoints:function(t){void 0===t&&(t=5);for(var e=[],n=0;n<=t;n++)e.push(this.getPointAt(n/t));return e},getLength:function(){var t=this.getLengths();return t[t.length-1]},getLengths:function(t){if(void 0===t&&(t=this.arcLengthDivisions),this.cacheArcLengths&&this.cacheArcLengths.length===t+1&&!this.needsUpdate)return this.cacheArcLengths;this.needsUpdate=!1;var e,n=[],r=this.getPoint(0),i=0;n.push(0);for(var a=1;a<=t;a++)i+=(e=this.getPoint(a/t)).distanceTo(r),n.push(i),r=e;return this.cacheArcLengths=n,n},updateArcLengths:function(){this.needsUpdate=!0,this.getLengths()},getUtoTmapping:function(t,e){var n,r=this.getLengths(),i=0,a=r.length;n=e||t*r[a-1];for(var o,s=0,l=a-1;s<=l;)if((o=r[i=Math.floor(s+(l-s)/2)]-n)<0)s=i+1;else{if(!(o>0)){l=i;break}l=i-1}if(r[i=l]===n)return i/(a-1);var c=r[i];return(i+(n-c)/(r[i+1]-c))/(a-1)},getTangent:function(t,e){var n=1e-4,r=t-n,i=t+n;r<0&&(r=0),i>1&&(i=1);var a=this.getPoint(r),o=this.getPoint(i),s=e||(a.isVector2?new yt:new Rt);return s.copy(o).sub(a).normalize(),s},getTangentAt:function(t,e){var n=this.getUtoTmapping(t);return this.getTangent(n,e)},computeFrenetFrames:function(t,e){for(var n=new Rt,r=[],i=[],a=[],o=new Rt,s=new re,l=0;l<=t;l++){var c=l/t;r[l]=this.getTangentAt(c,new Rt),r[l].normalize()}i[0]=new Rt,a[0]=new Rt;var u=Number.MAX_VALUE,h=Math.abs(r[0].x),d=Math.abs(r[0].y),p=Math.abs(r[0].z);h<=u&&(u=h,n.set(1,0,0)),d<=u&&(u=d,n.set(0,1,0)),p<=u&&n.set(0,0,1),o.crossVectors(r[0],n).normalize(),i[0].crossVectors(r[0],o),a[0].crossVectors(r[0],i[0]);for(var f=1;f<=t;f++){if(i[f]=i[f-1].clone(),a[f]=a[f-1].clone(),o.crossVectors(r[f-1],r[f]),o.length()>Number.EPSILON){o.normalize();var m=Math.acos(gt.clamp(r[f-1].dot(r[f]),-1,1));i[f].applyMatrix4(s.makeRotationAxis(o,m))}a[f].crossVectors(r[f],i[f])}if(!0===e){var v=Math.acos(gt.clamp(i[0].dot(i[t]),-1,1));v/=t,r[0].dot(o.crossVectors(i[0],i[t]))>0&&(v=-v);for(var g=1;g<=t;g++)i[g].applyMatrix4(s.makeRotationAxis(r[g],v*g)),a[g].crossVectors(r[g],i[g])}return{tangents:r,normals:i,binormals:a}},clone:function(){return(new this.constructor).copy(this)},copy:function(t){return this.arcLengthDivisions=t.arcLengthDivisions,this},toJSON:function(){var t={metadata:{version:4.5,type:"Curve",generator:"Curve.toJSON"}};return t.arcLengthDivisions=this.arcLengthDivisions,t.type=this.type,t},fromJSON:function(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}});var ol=function(t){function e(e,n,r,i,a,o,s,l){var c;return void 0===e&&(e=0),void 0===n&&(n=0),void 0===r&&(r=1),void 0===i&&(i=1),void 0===a&&(a=0),void 0===o&&(o=2*Math.PI),void 0===s&&(s=!1),void 0===l&&(l=0),(c=t.call(this)||this).type="EllipseCurve",c.aX=e,c.aY=n,c.xRadius=r,c.yRadius=i,c.aStartAngle=a,c.aEndAngle=o,c.aClockwise=s,c.aRotation=l,c}lt(e,t);var n=e.prototype;return n.getPoint=function(t,e){for(var n=e||new yt,r=2*Math.PI,i=this.aEndAngle-this.aStartAngle,a=Math.abs(i)r;)i-=r;i0?0:(Math.floor(Math.abs(l)/o)+1)*o:0===c&&l===o-1&&(l=o-2,c=1),this.closed||l>0?n=a[(l-1)%o]:(cl.subVectors(a[0],a[1]).add(a[0]),n=cl);var u=a[l%o],h=a[(l+1)%o];if(this.closed||l+2r.length-2?r.length-1:a+1],u=r[a>r.length-3?r.length-1:a+2];return n.set(fl(o,s.x,l.x,c.x,u.x),fl(o,s.y,l.y,c.y,u.y)),n},n.copy=function(e){t.prototype.copy.call(this,e),this.points=[];for(var n=0,r=e.points.length;n=e){var i=n[r]-e,a=this.curves[r],o=a.getLength(),s=0===o?0:1-i/o;return a.getPointAt(s)}r++}return null},n.getLength=function(){var t=this.getCurveLengths();return t[t.length-1]},n.updateArcLengths=function(){this.needsUpdate=!0,this.cacheLengths=null,this.getCurveLengths()},n.getCurveLengths=function(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;for(var t=[],e=0,n=0,r=this.curves.length;n1&&!n[n.length-1].equals(n[0])&&n.push(n[0]),n},n.copy=function(e){t.prototype.copy.call(this,e),this.curves=[];for(var n=0,r=e.curves.length;n0){var c=l.getPoint(0);c.equals(this.currentPoint)||this.lineTo(c.x,c.y)}this.curves.push(l);var u=l.getPoint(1);return this.currentPoint.copy(u),this},n.copy=function(e){return t.prototype.copy.call(this,e),this.currentPoint.copy(e.currentPoint),this},n.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.currentPoint=this.currentPoint.toArray(),e},n.fromJSON=function(e){return t.prototype.fromJSON.call(this,e),this.currentPoint.fromArray(e.currentPoint),this},e}(Tl),Al=function(t){function e(e){var n;return(n=t.call(this,e)||this).uuid=gt.generateUUID(),n.type="Shape",n.holes=[],n}lt(e,t);var n=e.prototype;return n.getPointsHoles=function(t){for(var e=[],n=0,r=this.holes.length;n0:r.vertexColors=t.vertexColors),void 0!==t.uniforms)for(var i in t.uniforms){var a=t.uniforms[i];switch(r.uniforms[i]={},a.type){case"t":r.uniforms[i].value=n(a.value);break;case"c":r.uniforms[i].value=(new Ke).setHex(a.value);break;case"v2":r.uniforms[i].value=(new yt).fromArray(a.value);break;case"v3":r.uniforms[i].value=(new Rt).fromArray(a.value);break;case"v4":r.uniforms[i].value=(new Tt).fromArray(a.value);break;case"m3":r.uniforms[i].value=(new _t).fromArray(a.value);break;case"m4":r.uniforms[i].value=(new re).fromArray(a.value);break;default:r.uniforms[i].value=a.value}}if(void 0!==t.defines&&(r.defines=t.defines),void 0!==t.vertexShader&&(r.vertexShader=t.vertexShader),void 0!==t.fragmentShader&&(r.fragmentShader=t.fragmentShader),void 0!==t.extensions)for(var o in t.extensions)r.extensions[o]=t.extensions[o];if(void 0!==t.shading&&(r.flatShading=1===t.shading),void 0!==t.size&&(r.size=t.size),void 0!==t.sizeAttenuation&&(r.sizeAttenuation=t.sizeAttenuation),void 0!==t.map&&(r.map=n(t.map)),void 0!==t.matcap&&(r.matcap=n(t.matcap)),void 0!==t.alphaMap&&(r.alphaMap=n(t.alphaMap)),void 0!==t.bumpMap&&(r.bumpMap=n(t.bumpMap)),void 0!==t.bumpScale&&(r.bumpScale=t.bumpScale),void 0!==t.normalMap&&(r.normalMap=n(t.normalMap)),void 0!==t.normalMapType&&(r.normalMapType=t.normalMapType),void 0!==t.normalScale){var s=t.normalScale;!1===Array.isArray(s)&&(s=[s,s]),r.normalScale=(new yt).fromArray(s)}return void 0!==t.displacementMap&&(r.displacementMap=n(t.displacementMap)),void 0!==t.displacementScale&&(r.displacementScale=t.displacementScale),void 0!==t.displacementBias&&(r.displacementBias=t.displacementBias),void 0!==t.roughnessMap&&(r.roughnessMap=n(t.roughnessMap)),void 0!==t.metalnessMap&&(r.metalnessMap=n(t.metalnessMap)),void 0!==t.emissiveMap&&(r.emissiveMap=n(t.emissiveMap)),void 0!==t.emissiveIntensity&&(r.emissiveIntensity=t.emissiveIntensity),void 0!==t.specularMap&&(r.specularMap=n(t.specularMap)),void 0!==t.envMap&&(r.envMap=n(t.envMap)),void 0!==t.envMapIntensity&&(r.envMapIntensity=t.envMapIntensity),void 0!==t.reflectivity&&(r.reflectivity=t.reflectivity),void 0!==t.refractionRatio&&(r.refractionRatio=t.refractionRatio),void 0!==t.lightMap&&(r.lightMap=n(t.lightMap)),void 0!==t.lightMapIntensity&&(r.lightMapIntensity=t.lightMapIntensity),void 0!==t.aoMap&&(r.aoMap=n(t.aoMap)),void 0!==t.aoMapIntensity&&(r.aoMapIntensity=t.aoMapIntensity),void 0!==t.gradientMap&&(r.gradientMap=n(t.gradientMap)),void 0!==t.clearcoatMap&&(r.clearcoatMap=n(t.clearcoatMap)),void 0!==t.clearcoatRoughnessMap&&(r.clearcoatRoughnessMap=n(t.clearcoatRoughnessMap)),void 0!==t.clearcoatNormalMap&&(r.clearcoatNormalMap=n(t.clearcoatNormalMap)),void 0!==t.clearcoatNormalScale&&(r.clearcoatNormalScale=(new yt).fromArray(t.clearcoatNormalScale)),void 0!==t.transmission&&(r.transmission=t.transmission),void 0!==t.transmissionMap&&(r.transmissionMap=n(t.transmissionMap)),r},n.setTextures=function(t){return this.textures=t,this},e}(Js),Zl={decodeText:function(t){if("undefined"!=typeof TextDecoder)return(new TextDecoder).decode(t);for(var e="",n=0,r=t.length;n0){var o=new Ys(e);(n=new el(o)).setCrossOrigin(this.crossOrigin);for(var s=0,l=t.length;sNumber.EPSILON){if(c<0&&(o=e[a],l=-l,s=e[i],c=-c),t.ys.y)continue;if(t.y===o.y){if(t.x===o.x)return!0}else{var u=c*(t.x-o.x)-l*(t.y-o.y);if(0===u)return!0;if(u<0)continue;r=!r}}else{if(t.y!==o.y)continue;if(s.x<=t.x&&t.x<=o.x||o.x<=t.x&&t.x<=s.x)return!0}}return r}var i,a,o,s=ns.isClockWise,l=this.subPaths;if(0===l.length)return[];if(!0===e)return n(l);var c=[];if(1===l.length)return a=l[0],(o=new Al).curves=a.curves,c.push(o),c;var u=!s(l[0].getPoints());u=t?!u:u;var h,d,p=[],f=[],m=[],v=0;f[v]=void 0,m[v]=[];for(var g=0,y=l.length;g1){for(var x=!1,_=[],b=0,w=f.length;b0&&(x||(m=p))}for(var C=0,P=f.length;C0){this.source.connect(this.filters[0]);for(var t=1,e=this.filters.length;t0){this.source.disconnect(this.filters[0]);for(var t=1,e=this.filters.length;t0&&this._mixBufferRegionAdditive(n,r,this._addIndex*e,1,e);for(var l=e,c=e+e;l!==c;++l)if(n[l]!==n[l+e]){o.setValue(n,r);break}},e.saveOriginalState=function(){var t=this.binding,e=this.buffer,n=this.valueSize,r=n*this._origIndex;t.getValue(e,r);for(var i=n,a=r;i!==a;++i)e[i]=e[r+i%n];this._setIdentity(),this.cumulativeWeight=0,this.cumulativeWeightAdditive=0},e.restoreOriginalState=function(){var t=3*this.valueSize;this.binding.setValue(this.buffer,t)},e._setAdditiveIdentityNumeric=function(){for(var t=this._addIndex*this.valueSize,e=t+this.valueSize,n=t;n=.5)for(var a=0;a!==i;++a)t[e+a]=t[n+a]},e._slerp=function(t,e,n,r){Lt.slerpFlat(t,e,t,e,t,n,r)},e._slerpAdditive=function(t,e,n,r,i){var a=this._workIndex*i;Lt.multiplyQuaternionsFlat(t,a,t,e,t,n),Lt.slerpFlat(t,e,t,e,t,a,r)},e._lerp=function(t,e,n,r,i){for(var a=1-r,o=0;o!==i;++o){var s=e+o;t[s]=t[s]*a+t[n+o]*r}},e._lerpAdditive=function(t,e,n,r,i){for(var a=0;a!==i;++a){var o=e+a;t[o]=t[o]+t[n+a]*r}},t}(),Pc="\\[\\]\\.:\\/",Dc=new RegExp("[\\[\\]\\.:\\/]","g"),Ic="[^\\[\\]\\.:\\/]",Nc="[^"+Pc.replace("\\.","")+"]",Bc=/((?:WC+[\/:])*)/.source.replace("WC",Ic),Oc=/(WCOD+)?/.source.replace("WCOD",Nc),zc=/(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace("WC",Ic),Fc=/\.(WC+)(?:\[(.+)\])?/.source.replace("WC",Ic),Hc=new RegExp("^"+Bc+Oc+zc+Fc+"$"),Gc=["material","materials","bones"];function Uc(t,e,n){var r=n||kc.parseTrackName(e);this._targetGroup=t,this._bindings=t.subscribe_(e,r)}function kc(t,e,n){this.path=e,this.parsedPath=n||kc.parseTrackName(e),this.node=kc.findNode(t,this.parsedPath.nodeName)||t,this.rootNode=t}Object.assign(Uc.prototype,{getValue:function(t,e){this.bind();var n=this._targetGroup.nCachedObjects_,r=this._bindings[n];void 0!==r&&r.getValue(t,e)},setValue:function(t,e){for(var n=this._bindings,r=this._targetGroup.nCachedObjects_,i=n.length;r!==i;++r)n[r].setValue(t,e)},bind:function(){for(var t=this._bindings,e=this._targetGroup.nCachedObjects_,n=t.length;e!==n;++e)t[e].bind()},unbind:function(){for(var t=this._bindings,e=this._targetGroup.nCachedObjects_,n=t.length;e!==n;++e)t[e].unbind()}}),Object.assign(kc,{Composite:Uc,create:function(t,e,n){return t&&t.isAnimationObjectGroup?new kc.Composite(t,e,n):new kc(t,e,n)},sanitizeNodeName:function(t){return t.replace(/\s/g,"_").replace(Dc,"")},parseTrackName:function(t){var e=Hc.exec(t);if(!e)throw new Error("PropertyBinding: Cannot parse trackName: "+t);var n={nodeName:e[2],objectName:e[3],objectIndex:e[4],propertyName:e[5],propertyIndex:e[6]},r=n.nodeName&&n.nodeName.lastIndexOf(".");if(void 0!==r&&-1!==r){var i=n.nodeName.substring(r+1);-1!==Gc.indexOf(i)&&(n.nodeName=n.nodeName.substring(0,r),n.objectName=i)}if(null===n.propertyName||0===n.propertyName.length)throw new Error("PropertyBinding: can not parse propertyName from trackName: "+t);return n},findNode:function(t,e){if(!e||""===e||"."===e||-1===e||e===t.name||e===t.uuid)return t;if(t.skeleton){var n=t.skeleton.getBoneByName(e);if(void 0!==n)return n}if(t.children){var r=function t(n){for(var r=0;r=i){var u=i++,h=t[u];e[h.uuid]=c,t[c]=h,e[l]=u,t[u]=s;for(var d=0,p=r;d!==p;++d){var f=n[d],m=f[u],v=f[c];f[c]=m,f[u]=v}}}this.nCachedObjects_=i},e.uncache=function(){for(var t=this._objects,e=this._indicesByUUID,n=this._bindings,r=n.length,i=this.nCachedObjects_,a=t.length,o=0,s=arguments.length;o!==s;++o){var l=arguments[o],c=l.uuid,u=e[c];if(void 0!==u)if(delete e[c],u0&&(e[b.uuid]=u),t[u]=b,t.pop();for(var w=0,M=r;w!==M;++w){var S=n[w];S[u]=S[_],S.pop()}}}this.nCachedObjects_=i},e.subscribe_=function(t,e){var n=this._bindingsIndicesByPath,r=n[t],i=this._bindings;if(void 0!==r)return i[r];var a=this._paths,o=this._parsedPaths,s=this._objects,l=s.length,c=this.nCachedObjects_,u=new Array(l);r=i.length,n[t]=r,a.push(t),o.push(e),i.push(u);for(var h=c,d=s.length;h!==d;++h){var p=s[h];u[h]=new kc(p,t,e)}return u},e.unsubscribe_=function(t){var e=this._bindingsIndicesByPath,n=e[t];if(void 0!==n){var r=this._paths,i=this._parsedPaths,a=this._bindings,o=a.length-1,s=a[o];e[t[o]]=n,a[n]=s,a.pop(),i[n]=i[o],i.pop(),r[n]=r[o],r.pop()}},t}();Vc.prototype.isAnimationObjectGroup=!0;var Wc=function(){function t(t,e,n,r){void 0===n&&(n=null),void 0===r&&(r=e.blendMode),this._mixer=t,this._clip=e,this._localRoot=n,this.blendMode=r;for(var i=e.tracks,a=i.length,o=new Array(a),s={endingStart:V,endingEnd:V},l=0;l!==a;++l){var c=i[l].createInterpolant(null);o[l]=c,c.settings=s}this._interpolantSettings=s,this._interpolants=o,this._propertyBindings=new Array(a),this._cacheIndex=null,this._byClipCacheIndex=null,this._timeScaleInterpolant=null,this._weightInterpolant=null,this.loop=2201,this._loopCount=-1,this._startTime=null,this.time=0,this.timeScale=1,this._effectiveTimeScale=1,this.weight=1,this._effectiveWeight=1,this.repetitions=1/0,this.paused=!1,this.enabled=!0,this.clampWhenFinished=!1,this.zeroSlopeAtStart=!0,this.zeroSlopeAtEnd=!0}var e=t.prototype;return e.play=function(){return this._mixer._activateAction(this),this},e.stop=function(){return this._mixer._deactivateAction(this),this.reset()},e.reset=function(){return this.paused=!1,this.enabled=!0,this.time=0,this._loopCount=-1,this._startTime=null,this.stopFading().stopWarping()},e.isRunning=function(){return this.enabled&&!this.paused&&0!==this.timeScale&&null===this._startTime&&this._mixer._isActiveAction(this)},e.isScheduled=function(){return this._mixer._isActiveAction(this)},e.startAt=function(t){return this._startTime=t,this},e.setLoop=function(t,e){return this.loop=t,this.repetitions=e,this},e.setEffectiveWeight=function(t){return this.weight=t,this._effectiveWeight=this.enabled?t:0,this.stopFading()},e.getEffectiveWeight=function(){return this._effectiveWeight},e.fadeIn=function(t){return this._scheduleFading(t,0,1)},e.fadeOut=function(t){return this._scheduleFading(t,1,0)},e.crossFadeFrom=function(t,e,n){if(t.fadeOut(e),this.fadeIn(e),n){var r=this._clip.duration,i=t._clip.duration,a=i/r,o=r/i;t.warp(1,a,e),this.warp(o,1,e)}return this},e.crossFadeTo=function(t,e,n){return t.crossFadeFrom(this,e,n)},e.stopFading=function(){var t=this._weightInterpolant;return null!==t&&(this._weightInterpolant=null,this._mixer._takeBackControlInterpolant(t)),this},e.setEffectiveTimeScale=function(t){return this.timeScale=t,this._effectiveTimeScale=this.paused?0:t,this.stopWarping()},e.getEffectiveTimeScale=function(){return this._effectiveTimeScale},e.setDuration=function(t){return this.timeScale=this._clip.duration/t,this.stopWarping()},e.syncWith=function(t){return this.time=t.time,this.timeScale=t.timeScale,this.stopWarping()},e.halt=function(t){return this.warp(this._effectiveTimeScale,0,t)},e.warp=function(t,e,n){var r=this._mixer,i=r.time,a=this.timeScale,o=this._timeScaleInterpolant;null===o&&(o=r._lendControlInterpolant(),this._timeScaleInterpolant=o);var s=o.parameterPositions,l=o.sampleValues;return s[0]=i,s[1]=i+n,l[0]=t/a,l[1]=e/a,this},e.stopWarping=function(){var t=this._timeScaleInterpolant;return null!==t&&(this._timeScaleInterpolant=null,this._mixer._takeBackControlInterpolant(t)),this},e.getMixer=function(){return this._mixer},e.getClip=function(){return this._clip},e.getRoot=function(){return this._localRoot||this._mixer._root},e._update=function(t,e,n,r){if(this.enabled){var i=this._startTime;if(null!==i){var a=(t-i)*n;if(a<0||0===n)return;this._startTime=null,e=n*a}e*=this._updateTimeScale(t);var o=this._updateTime(e),s=this._updateWeight(t);if(s>0){var l=this._interpolants,c=this._propertyBindings;switch(this.blendMode){case X:for(var u=0,h=l.length;u!==h;++u)l[u].evaluate(o),c[u].accumulateAdditive(s);break;case q:default:for(var d=0,p=l.length;d!==p;++d)l[d].evaluate(o),c[d].accumulate(r,s)}}}else this._updateWeight(t)},e._updateWeight=function(t){var e=0;if(this.enabled){e=this.weight;var n=this._weightInterpolant;if(null!==n){var r=n.evaluate(t)[0];e*=r,t>n.parameterPositions[1]&&(this.stopFading(),0===r&&(this.enabled=!1))}}return this._effectiveWeight=e,e},e._updateTimeScale=function(t){var e=0;if(!this.paused){e=this.timeScale;var n=this._timeScaleInterpolant;if(null!==n)e*=n.evaluate(t)[0],t>n.parameterPositions[1]&&(this.stopWarping(),0===e?this.paused=!0:this.timeScale=e)}return this._effectiveTimeScale=e,e},e._updateTime=function(t){var e=this._clip.duration,n=this.loop,r=this.time+t,i=this._loopCount,a=2202===n;if(0===t)return-1===i?r:a&&1==(1&i)?e-r:r;if(2200===n){-1===i&&(this._loopCount=0,this._setEndings(!0,!0,!1));t:{if(r>=e)r=e;else{if(!(r<0)){this.time=r;break t}r=0}this.clampWhenFinished?this.paused=!0:this.enabled=!1,this.time=r,this._mixer.dispatchEvent({type:"finished",action:this,direction:t<0?-1:1})}}else{if(-1===i&&(t>=0?(i=0,this._setEndings(!0,0===this.repetitions,a)):this._setEndings(0===this.repetitions,!0,a)),r>=e||r<0){var o=Math.floor(r/e);r-=e*o,i+=Math.abs(o);var s=this.repetitions-i;if(s<=0)this.clampWhenFinished?this.paused=!0:this.enabled=!1,r=t>0?e:0,this.time=r,this._mixer.dispatchEvent({type:"finished",action:this,direction:t>0?1:-1});else{if(1===s){var l=t<0;this._setEndings(l,!l,a)}else this._setEndings(!1,!1,a);this._loopCount=i,this.time=r,this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:o})}}else this.time=r;if(a&&1==(1&i))return e-r}return r},e._setEndings=function(t,e,n){var r=this._interpolantSettings;n?(r.endingStart=W,r.endingEnd=W):(r.endingStart=t?this.zeroSlopeAtStart?W:V:j,r.endingEnd=e?this.zeroSlopeAtEnd?W:V:j)},e._scheduleFading=function(t,e,n){var r=this._mixer,i=r.time,a=this._weightInterpolant;null===a&&(a=r._lendControlInterpolant(),this._weightInterpolant=a);var o=a.parameterPositions,s=a.sampleValues;return o[0]=i,s[0]=e,o[1]=i+t,s[1]=n,this},t}(),jc=function(t){function e(e){var n;return(n=t.call(this)||this)._root=e,n._initMemoryManager(),n._accuIndex=0,n.time=0,n.timeScale=1,n}lt(e,t);var n=e.prototype;return n._bindAction=function(t,e){var n=t._localRoot||this._root,r=t._clip.tracks,i=r.length,a=t._propertyBindings,o=t._interpolants,s=n.uuid,l=this._bindingsByRootAndName,c=l[s];void 0===c&&(c={},l[s]=c);for(var u=0;u!==i;++u){var h=r[u],d=h.name,p=c[d];if(void 0!==p)a[u]=p;else{if(void 0!==(p=a[u])){null===p._cacheIndex&&(++p.referenceCount,this._addInactiveBinding(p,s,d));continue}var f=e&&e._propertyBindings[u].binding.parsedPath;++(p=new Cc(kc.create(n,d,f),h.ValueTypeName,h.getValueSize())).referenceCount,this._addInactiveBinding(p,s,d),a[u]=p}o[u].resultBuffer=p.buffer}},n._activateAction=function(t){if(!this._isActiveAction(t)){if(null===t._cacheIndex){var e=(t._localRoot||this._root).uuid,n=t._clip.uuid,r=this._actionsByClip[n];this._bindAction(t,r&&r.knownActions[0]),this._addInactiveAction(t,n,e)}for(var i=t._propertyBindings,a=0,o=i.length;a!==o;++a){var s=i[a];0==s.useCount++&&(this._lendBinding(s),s.saveOriginalState())}this._lendAction(t)}},n._deactivateAction=function(t){if(this._isActiveAction(t)){for(var e=t._propertyBindings,n=0,r=e.length;n!==r;++n){var i=e[n];0==--i.useCount&&(i.restoreOriginalState(),this._takeBackBinding(i))}this._takeBackAction(t)}},n._initMemoryManager=function(){this._actions=[],this._nActiveActions=0,this._actionsByClip={},this._bindings=[],this._nActiveBindings=0,this._bindingsByRootAndName={},this._controlInterpolants=[],this._nActiveControlInterpolants=0;var t=this;this.stats={actions:{get total(){return t._actions.length},get inUse(){return t._nActiveActions}},bindings:{get total(){return t._bindings.length},get inUse(){return t._nActiveBindings}},controlInterpolants:{get total(){return t._controlInterpolants.length},get inUse(){return t._nActiveControlInterpolants}}}},n._isActiveAction=function(t){var e=t._cacheIndex;return null!==e&&e=0;--e)t[e].stop();return this},n.update=function(t){t*=this.timeScale;for(var e=this._actions,n=this._nActiveActions,r=this.time+=t,i=Math.sign(t),a=this._accuIndex^=1,o=0;o!==n;++o){e[o]._update(r,t,i,a)}for(var s=this._bindings,l=this._nActiveBindings,c=0;c!==l;++c)s[c].apply(a);return this},n.setTime=function(t){this.time=0;for(var e=0;ethis.max.x||t.ythis.max.y)},e.containsBox=function(t){return this.min.x<=t.min.x&&t.max.x<=this.max.x&&this.min.y<=t.min.y&&t.max.y<=this.max.y},e.getParameter=function(t,e){return void 0===e&&(console.warn("THREE.Box2: .getParameter() target is now required"),e=new yt),e.set((t.x-this.min.x)/(this.max.x-this.min.x),(t.y-this.min.y)/(this.max.y-this.min.y))},e.intersectsBox=function(t){return!(t.max.xthis.max.x||t.max.ythis.max.y)},e.clampPoint=function(t,e){return void 0===e&&(console.warn("THREE.Box2: .clampPoint() target is now required"),e=new yt),e.copy(t).clamp(this.min,this.max)},e.distanceToPoint=function(t){return tu.copy(t).clamp(this.min,this.max).sub(t).length()},e.intersect=function(t){return this.min.max(t.min),this.max.min(t.max),this},e.union=function(t){return this.min.min(t.min),this.max.max(t.max),this},e.translate=function(t){return this.min.add(t),this.max.add(t),this},e.equals=function(t){return t.min.equals(this.min)&&t.max.equals(this.max)},t}();eu.prototype.isBox2=!0;var nu=new Rt,ru=new Rt,iu=function(){function t(t,e){void 0===t&&(t=new Rt),void 0===e&&(e=new Rt),this.start=t,this.end=e}var e=t.prototype;return e.set=function(t,e){return this.start.copy(t),this.end.copy(e),this},e.copy=function(t){return this.start.copy(t.start),this.end.copy(t.end),this},e.getCenter=function(t){return void 0===t&&(console.warn("THREE.Line3: .getCenter() target is now required"),t=new Rt),t.addVectors(this.start,this.end).multiplyScalar(.5)},e.delta=function(t){return void 0===t&&(console.warn("THREE.Line3: .delta() target is now required"),t=new Rt),t.subVectors(this.end,this.start)},e.distanceSq=function(){return this.start.distanceToSquared(this.end)},e.distance=function(){return this.start.distanceTo(this.end)},e.at=function(t,e){return void 0===e&&(console.warn("THREE.Line3: .at() target is now required"),e=new Rt),this.delta(e).multiplyScalar(t).add(this.start)},e.closestPointToPointParameter=function(t,e){nu.subVectors(t,this.start),ru.subVectors(this.end,this.start);var n=ru.dot(ru),r=ru.dot(nu)/n;return e&&(r=gt.clamp(r,0,1)),r},e.closestPointToPoint=function(t,e,n){var r=this.closestPointToPointParameter(t,e);return void 0===n&&(console.warn("THREE.Line3: .closestPointToPoint() target is now required"),n=new Rt),this.delta(n).multiplyScalar(r).add(this.start)},e.applyMatrix4=function(t){return this.start.applyMatrix4(t),this.end.applyMatrix4(t),this},e.equals=function(t){return t.start.equals(this.start)&&t.end.equals(this.end)},e.clone=function(){return(new this.constructor).copy(this)},t}();function au(t){Le.call(this),this.material=t,this.render=function(){},this.hasPositions=!1,this.hasNormals=!1,this.hasColors=!1,this.hasUvs=!1,this.positionArray=null,this.normalArray=null,this.colorArray=null,this.uvArray=null,this.count=0}au.prototype=Object.create(Le.prototype),au.prototype.constructor=au,au.prototype.isImmediateRenderObject=!0;var ou=new Rt,su=function(t){function e(e,n){var r;(r=t.call(this)||this).light=e,r.light.updateMatrixWorld(),r.matrix=e.matrixWorld,r.matrixAutoUpdate=!1,r.color=n;for(var i=new Sn,a=[0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,-1,0,1,0,0,0,0,1,1,0,0,0,0,-1,1],o=0,s=1;o<32;o++,s++){var l=o/32*Math.PI*2,c=s/32*Math.PI*2;a.push(Math.cos(l),Math.sin(l),1,Math.cos(c),Math.sin(c),1)}i.setAttribute("position",new dn(a,3));var u=new Ka({fog:!1,toneMapped:!1});return r.cone=new so(i,u),r.add(r.cone),r.update(),r}lt(e,t);var n=e.prototype;return n.dispose=function(){this.cone.geometry.dispose(),this.cone.material.dispose()},n.update=function(){this.light.updateMatrixWorld();var t=this.light.distance?this.light.distance:1e3,e=t*Math.tan(this.light.angle);this.cone.scale.set(e,e,t),ou.setFromMatrixPosition(this.light.target.matrixWorld),this.cone.lookAt(ou),void 0!==this.color?this.cone.material.color.set(this.color):this.cone.material.color.copy(this.light.color)},e}(Le),lu=new Rt,cu=new re,uu=new re,hu=function(t){function e(e){for(var n,r=du(e),i=new Sn,a=[],o=[],s=new Ke(0,0,1),l=new Ke(0,1,0),c=0;c.99999)this.quaternion.set(0,0,0,1);else if(t.y<-.99999)this.quaternion.set(1,0,0,0);else{Bu.set(t.z,0,-t.x).normalize();var e=Math.acos(t.y);this.quaternion.setFromAxisAngle(Bu,e)}},n.setLength=function(t,e,n){void 0===e&&(e=.2*t),void 0===n&&(n=.2*e),this.line.scale.set(1,Math.max(1e-4,t-e),1),this.line.updateMatrix(),this.cone.scale.set(n,e,n),this.cone.position.y=t,this.cone.updateMatrix()},n.setColor=function(t){this.line.material.color.set(t),this.cone.material.color.set(t)},n.copy=function(e){return t.prototype.copy.call(this,e,!1),this.line.copy(e.line),this.cone.copy(e.cone),this},e}(Le),zu=function(t){function e(e){var n;void 0===e&&(e=1);var r=[0,0,0,e,0,0,0,0,0,0,e,0,0,0,0,0,0,e],i=new Sn;i.setAttribute("position",new dn(r,3)),i.setAttribute("color",new dn([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1],3));var a=new Ka({vertexColors:!0,toneMapped:!1});return(n=t.call(this,i,a)||this).type="AxesHelper",n}return lt(e,t),e}(so),Fu=new Float32Array(1),Hu=new Int32Array(Fu.buffer),Gu={toHalfFloat:function(t){Fu[0]=t;var e=Hu[0],n=e>>16&32768,r=e>>12&2047,i=e>>23&255;return i<103?n:i>142?(n|=31744,n|=(255==i?0:1)&&8388607&e):i<113?n|=((r|=2048)>>114-i)+(r>>113-i&1):(n|=i-112<<10|r>>1,n+=1&r)}},Uu=Math.pow(2,8),ku=[.125,.215,.35,.446,.526,.582],Vu=5+ku.length,Wu=20,ju=((Cu={})[3e3]=0,Cu[3001]=1,Cu[3002]=2,Cu[3004]=3,Cu[3005]=4,Cu[3006]=5,Cu[3007]=6,Cu),qu=new $e({side:1,depthWrite:!1,depthTest:!1}),Xu=new kn(new Wn,qu),Yu=new Ul,Zu=sh(),Ju=Zu._lodPlanes,Qu=Zu._sizeLods,Ku=Zu._sigmas,$u=new Ke,th=null,eh=(1+Math.sqrt(5))/2,nh=1/eh,rh=[new Rt(1,1,1),new Rt(-1,1,1),new Rt(1,1,-1),new Rt(-1,1,-1),new Rt(0,eh,nh),new Rt(0,eh,-nh),new Rt(nh,0,eh),new Rt(-nh,0,eh),new Rt(eh,nh,0),new Rt(-eh,nh,0)];function ih(t){var e=Math.max(t.r,t.g,t.b),n=Math.min(Math.max(Math.ceil(Math.log2(e)),-128),127);return t.multiplyScalar(Math.pow(2,-n)),(n+128)/255}var ah=function(){function t(t){var e,n,r;this._renderer=t,this._pingPongRenderTarget=null,this._blurMaterial=(e=Wu,n=new Float32Array(e),r=new Rt(0,1,0),new ws({name:"SphericalGaussianBlur",defines:{n:e},uniforms:{envMap:{value:null},samples:{value:1},weights:{value:n},latitudinal:{value:!1},dTheta:{value:0},mipInt:{value:0},poleAxis:{value:r},inputEncoding:{value:ju[3e3]},outputEncoding:{value:ju[3e3]}},vertexShader:"\n\n\t\tprecision mediump float;\n\t\tprecision mediump int;\n\n\t\tattribute vec3 position;\n\t\tattribute vec2 uv;\n\t\tattribute float faceIndex;\n\n\t\tvarying vec3 vOutputDirection;\n\n\t\t// RH coordinate system; PMREM face-indexing convention\n\t\tvec3 getDirection( vec2 uv, float face ) {\n\n\t\t\tuv = 2.0 * uv - 1.0;\n\n\t\t\tvec3 direction = vec3( uv, 1.0 );\n\n\t\t\tif ( face == 0.0 ) {\n\n\t\t\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\n\t\t\t} else if ( face == 1.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\n\t\t\t} else if ( face == 2.0 ) {\n\n\t\t\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\n\t\t\t} else if ( face == 3.0 ) {\n\n\t\t\t\tdirection = direction.zyx;\n\t\t\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\n\t\t\t} else if ( face == 4.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\n\t\t\t} else if ( face == 5.0 ) {\n\n\t\t\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\n\t\t\t}\n\n\t\t\treturn direction;\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvOutputDirection = getDirection( uv, faceIndex );\n\t\t\tgl_Position = vec4( position, 1.0 );\n\n\t\t}\n\t",fragmentShader:"\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\t\t\tuniform int samples;\n\t\t\tuniform float weights[ n ];\n\t\t\tuniform bool latitudinal;\n\t\t\tuniform float dTheta;\n\t\t\tuniform float mipInt;\n\t\t\tuniform vec3 poleAxis;\n\n\t\t\t\n\n\t\tuniform int inputEncoding;\n\t\tuniform int outputEncoding;\n\n\t\t#include \n\n\t\tvec4 inputTexelToLinear( vec4 value ) {\n\n\t\t\tif ( inputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( inputEncoding == 1 ) {\n\n\t\t\t\treturn sRGBToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 2 ) {\n\n\t\t\t\treturn RGBEToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 3 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 7.0 );\n\n\t\t\t} else if ( inputEncoding == 4 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 16.0 );\n\n\t\t\t} else if ( inputEncoding == 5 ) {\n\n\t\t\t\treturn RGBDToLinear( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn GammaToLinear( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 linearToOutputTexel( vec4 value ) {\n\n\t\t\tif ( outputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( outputEncoding == 1 ) {\n\n\t\t\t\treturn LinearTosRGB( value );\n\n\t\t\t} else if ( outputEncoding == 2 ) {\n\n\t\t\t\treturn LinearToRGBE( value );\n\n\t\t\t} else if ( outputEncoding == 3 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 7.0 );\n\n\t\t\t} else if ( outputEncoding == 4 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 16.0 );\n\n\t\t\t} else if ( outputEncoding == 5 ) {\n\n\t\t\t\treturn LinearToRGBD( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn LinearToGamma( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 envMapTexelToLinear( vec4 color ) {\n\n\t\t\treturn inputTexelToLinear( color );\n\n\t\t}\n\t\n\n\t\t\t#define ENVMAP_TYPE_CUBE_UV\n\t\t\t#include \n\n\t\t\tvec3 getSample( float theta, vec3 axis ) {\n\n\t\t\t\tfloat cosTheta = cos( theta );\n\t\t\t\t// Rodrigues' axis-angle rotation\n\t\t\t\tvec3 sampleDirection = vOutputDirection * cosTheta\n\t\t\t\t\t+ cross( axis, vOutputDirection ) * sin( theta )\n\t\t\t\t\t+ axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta );\n\n\t\t\t\treturn bilinearCubeUV( envMap, sampleDirection, mipInt );\n\n\t\t\t}\n\n\t\t\tvoid main() {\n\n\t\t\t\tvec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection );\n\n\t\t\t\tif ( all( equal( axis, vec3( 0.0 ) ) ) ) {\n\n\t\t\t\t\taxis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x );\n\n\t\t\t\t}\n\n\t\t\t\taxis = normalize( axis );\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t\t\t\tgl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis );\n\n\t\t\t\tfor ( int i = 1; i < n; i++ ) {\n\n\t\t\t\t\tif ( i >= samples ) {\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat theta = dTheta * float( i );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( theta, axis );\n\n\t\t\t\t}\n\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t",blending:0,depthTest:!1,depthWrite:!1})),this._equirectShader=null,this._cubemapShader=null,this._compileMaterial(this._blurMaterial)}var e=t.prototype;return e.fromScene=function(t,e,n,r){void 0===e&&(e=0),void 0===n&&(n=.1),void 0===r&&(r=100),th=this._renderer.getRenderTarget();var i=this._allocateTargets();return this._sceneToCubeUV(t,n,r,i),e>0&&this._blur(i,0,0,e),this._applyPMREM(i),this._cleanup(i),i},e.fromEquirectangular=function(t){return this._fromTexture(t)},e.fromCubemap=function(t){return this._fromTexture(t)},e.compileCubemapShader=function(){null===this._cubemapShader&&(this._cubemapShader=hh(),this._compileMaterial(this._cubemapShader))},e.compileEquirectangularShader=function(){null===this._equirectShader&&(this._equirectShader=uh(),this._compileMaterial(this._equirectShader))},e.dispose=function(){this._blurMaterial.dispose(),null!==this._cubemapShader&&this._cubemapShader.dispose(),null!==this._equirectShader&&this._equirectShader.dispose();for(var t=0;t2?Uu:0,Uu,Uu),s.setRenderTarget(r),h&&s.render(Xu,i),s.render(t,i)}s.toneMapping=u,s.outputEncoding=c,s.autoClear=l},e._textureToCubeUV=function(t,e){var n=this._renderer;t.isCubeTexture?null==this._cubemapShader&&(this._cubemapShader=hh()):null==this._equirectShader&&(this._equirectShader=uh());var r=t.isCubeTexture?this._cubemapShader:this._equirectShader,i=new kn(Ju[0],r),a=r.uniforms;a.envMap.value=t,t.isCubeTexture||a.texelSize.value.set(1/t.image.width,1/t.image.height),a.inputEncoding.value=ju[t.encoding],a.outputEncoding.value=ju[e.texture.encoding],ch(e,0,0,3*Uu,2*Uu),n.setRenderTarget(e),n.render(i,Yu)},e._applyPMREM=function(t){var e=this._renderer,n=e.autoClear;e.autoClear=!1;for(var r=1;rWu&&console.warn("sigmaRadians, "+i+", is too large and will clip, as it requested "+f+" samples when the maximum is set to "+Wu);for(var m=[],v=0,g=0;g4?r-8+4:0),3*b,2*b),s.setRenderTarget(e),s.render(c,Yu)},t}();function oh(t){return void 0!==t&&t.type===_&&(t.encoding===Y||t.encoding===Z||t.encoding===J)}function sh(){for(var t=[],e=[],n=[],r=8,i=0;i4?o=ku[i-8+4-1]:0==i&&(o=0),n.push(o);for(var s=1/(a-1),l=-s/2,c=1+s/2,u=[l,l,c,l,c,c,l,l,c,c,l,c],h=new Float32Array(108),d=new Float32Array(72),p=new Float32Array(36),f=0;f<6;f++){var m=f%3*2/3-1,v=f>2?0:-1,g=[m,v,0,m+2/3,v,0,m+2/3,v+1,0,m,v,0,m+2/3,v+1,0,m,v+1,0];h.set(g,18*f),d.set(u,12*f);var y=[f,f,f,f,f,f];p.set(y,6*f)}var x=new Sn;x.setAttribute("position",new nn(h,3)),x.setAttribute("uv",new nn(d,2)),x.setAttribute("faceIndex",new nn(p,1)),t.push(x),r>4&&r--}return{_lodPlanes:t,_sizeLods:e,_sigmas:n}}function lh(t){var e=new Et(3*Uu,3*Uu,t);return e.texture.mapping=c,e.texture.name="PMREM.cubeUv",e.scissorTest=!0,e}function ch(t,e,n,r,i){t.viewport.set(e,n,r,i),t.scissor.set(e,n,r,i)}function uh(){var t=new yt(1,1);return new ws({name:"EquirectangularToCubeUV",uniforms:{envMap:{value:null},texelSize:{value:t},inputEncoding:{value:ju[3e3]},outputEncoding:{value:ju[3e3]}},vertexShader:"\n\n\t\tprecision mediump float;\n\t\tprecision mediump int;\n\n\t\tattribute vec3 position;\n\t\tattribute vec2 uv;\n\t\tattribute float faceIndex;\n\n\t\tvarying vec3 vOutputDirection;\n\n\t\t// RH coordinate system; PMREM face-indexing convention\n\t\tvec3 getDirection( vec2 uv, float face ) {\n\n\t\t\tuv = 2.0 * uv - 1.0;\n\n\t\t\tvec3 direction = vec3( uv, 1.0 );\n\n\t\t\tif ( face == 0.0 ) {\n\n\t\t\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\n\t\t\t} else if ( face == 1.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\n\t\t\t} else if ( face == 2.0 ) {\n\n\t\t\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\n\t\t\t} else if ( face == 3.0 ) {\n\n\t\t\t\tdirection = direction.zyx;\n\t\t\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\n\t\t\t} else if ( face == 4.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\n\t\t\t} else if ( face == 5.0 ) {\n\n\t\t\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\n\t\t\t}\n\n\t\t\treturn direction;\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvOutputDirection = getDirection( uv, faceIndex );\n\t\t\tgl_Position = vec4( position, 1.0 );\n\n\t\t}\n\t",fragmentShader:"\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\t\t\tuniform vec2 texelSize;\n\n\t\t\t\n\n\t\tuniform int inputEncoding;\n\t\tuniform int outputEncoding;\n\n\t\t#include \n\n\t\tvec4 inputTexelToLinear( vec4 value ) {\n\n\t\t\tif ( inputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( inputEncoding == 1 ) {\n\n\t\t\t\treturn sRGBToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 2 ) {\n\n\t\t\t\treturn RGBEToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 3 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 7.0 );\n\n\t\t\t} else if ( inputEncoding == 4 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 16.0 );\n\n\t\t\t} else if ( inputEncoding == 5 ) {\n\n\t\t\t\treturn RGBDToLinear( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn GammaToLinear( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 linearToOutputTexel( vec4 value ) {\n\n\t\t\tif ( outputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( outputEncoding == 1 ) {\n\n\t\t\t\treturn LinearTosRGB( value );\n\n\t\t\t} else if ( outputEncoding == 2 ) {\n\n\t\t\t\treturn LinearToRGBE( value );\n\n\t\t\t} else if ( outputEncoding == 3 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 7.0 );\n\n\t\t\t} else if ( outputEncoding == 4 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 16.0 );\n\n\t\t\t} else if ( outputEncoding == 5 ) {\n\n\t\t\t\treturn LinearToRGBD( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn LinearToGamma( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 envMapTexelToLinear( vec4 color ) {\n\n\t\t\treturn inputTexelToLinear( color );\n\n\t\t}\n\t\n\n\t\t\t#include \n\n\t\t\tvoid main() {\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\n\t\t\t\tvec3 outputDirection = normalize( vOutputDirection );\n\t\t\t\tvec2 uv = equirectUv( outputDirection );\n\n\t\t\t\tvec2 f = fract( uv / texelSize - 0.5 );\n\t\t\t\tuv -= f * texelSize;\n\t\t\t\tvec3 tl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.x += texelSize.x;\n\t\t\t\tvec3 tr = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.y += texelSize.y;\n\t\t\t\tvec3 br = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.x -= texelSize.x;\n\t\t\t\tvec3 bl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\n\t\t\t\tvec3 tm = mix( tl, tr, f.x );\n\t\t\t\tvec3 bm = mix( bl, br, f.x );\n\t\t\t\tgl_FragColor.rgb = mix( tm, bm, f.y );\n\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t",blending:0,depthTest:!1,depthWrite:!1})}function hh(){return new ws({name:"CubemapToCubeUV",uniforms:{envMap:{value:null},inputEncoding:{value:ju[3e3]},outputEncoding:{value:ju[3e3]}},vertexShader:"\n\n\t\tprecision mediump float;\n\t\tprecision mediump int;\n\n\t\tattribute vec3 position;\n\t\tattribute vec2 uv;\n\t\tattribute float faceIndex;\n\n\t\tvarying vec3 vOutputDirection;\n\n\t\t// RH coordinate system; PMREM face-indexing convention\n\t\tvec3 getDirection( vec2 uv, float face ) {\n\n\t\t\tuv = 2.0 * uv - 1.0;\n\n\t\t\tvec3 direction = vec3( uv, 1.0 );\n\n\t\t\tif ( face == 0.0 ) {\n\n\t\t\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\n\t\t\t} else if ( face == 1.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\n\t\t\t} else if ( face == 2.0 ) {\n\n\t\t\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\n\t\t\t} else if ( face == 3.0 ) {\n\n\t\t\t\tdirection = direction.zyx;\n\t\t\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\n\t\t\t} else if ( face == 4.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\n\t\t\t} else if ( face == 5.0 ) {\n\n\t\t\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\n\t\t\t}\n\n\t\t\treturn direction;\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvOutputDirection = getDirection( uv, faceIndex );\n\t\t\tgl_Position = vec4( position, 1.0 );\n\n\t\t}\n\t",fragmentShader:"\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform samplerCube envMap;\n\n\t\t\t\n\n\t\tuniform int inputEncoding;\n\t\tuniform int outputEncoding;\n\n\t\t#include \n\n\t\tvec4 inputTexelToLinear( vec4 value ) {\n\n\t\t\tif ( inputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( inputEncoding == 1 ) {\n\n\t\t\t\treturn sRGBToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 2 ) {\n\n\t\t\t\treturn RGBEToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 3 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 7.0 );\n\n\t\t\t} else if ( inputEncoding == 4 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 16.0 );\n\n\t\t\t} else if ( inputEncoding == 5 ) {\n\n\t\t\t\treturn RGBDToLinear( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn GammaToLinear( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 linearToOutputTexel( vec4 value ) {\n\n\t\t\tif ( outputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( outputEncoding == 1 ) {\n\n\t\t\t\treturn LinearTosRGB( value );\n\n\t\t\t} else if ( outputEncoding == 2 ) {\n\n\t\t\t\treturn LinearToRGBE( value );\n\n\t\t\t} else if ( outputEncoding == 3 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 7.0 );\n\n\t\t\t} else if ( outputEncoding == 4 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 16.0 );\n\n\t\t\t} else if ( outputEncoding == 5 ) {\n\n\t\t\t\treturn LinearToRGBD( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn LinearToGamma( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 envMapTexelToLinear( vec4 color ) {\n\n\t\t\treturn inputTexelToLinear( color );\n\n\t\t}\n\t\n\n\t\t\tvoid main() {\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t\t\t\tgl_FragColor.rgb = envMapTexelToLinear( textureCube( envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ) ) ).rgb;\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t",blending:0,depthTest:!1,depthWrite:!1})}al.create=function(t,e){return console.log("THREE.Curve.create() has been deprecated"),t.prototype=Object.create(al.prototype),t.prototype.constructor=t,t.prototype.getPoint=e,t},El.prototype.fromPoints=function(t){return console.warn("THREE.Path: .fromPoints() has been renamed to .setFromPoints()."),this.setFromPoints(t)},yu.prototype.setColors=function(){console.error("THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.")},hu.prototype.update=function(){console.error("THREE.SkeletonHelper: update() no longer needs to be called.")},Js.prototype.extractUrlBase=function(t){return console.warn("THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead."),Zl.extractUrlBase(t)},Js.Handlers={add:function(){console.error("THREE.Loader: Handlers.add() has been removed. Use LoadingManager.addHandler() instead.")},get:function(){console.error("THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead.")}},eu.prototype.center=function(t){return console.warn("THREE.Box2: .center() has been renamed to .getCenter()."),this.getCenter(t)},eu.prototype.empty=function(){return console.warn("THREE.Box2: .empty() has been renamed to .isEmpty()."),this.isEmpty()},eu.prototype.isIntersectionBox=function(t){return console.warn("THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox()."),this.intersectsBox(t)},eu.prototype.size=function(t){return console.warn("THREE.Box2: .size() has been renamed to .getSize()."),this.getSize(t)},Dt.prototype.center=function(t){return console.warn("THREE.Box3: .center() has been renamed to .getCenter()."),this.getCenter(t)},Dt.prototype.empty=function(){return console.warn("THREE.Box3: .empty() has been renamed to .isEmpty()."),this.isEmpty()},Dt.prototype.isIntersectionBox=function(t){return console.warn("THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox()."),this.intersectsBox(t)},Dt.prototype.isIntersectionSphere=function(t){return console.warn("THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere()."),this.intersectsSphere(t)},Dt.prototype.size=function(t){return console.warn("THREE.Box3: .size() has been renamed to .getSize()."),this.getSize(t)},Yt.prototype.empty=function(){return console.warn("THREE.Sphere: .empty() has been renamed to .isEmpty()."),this.isEmpty()},ir.prototype.setFromMatrix=function(t){return console.warn("THREE.Frustum: .setFromMatrix() has been renamed to .setFromProjectionMatrix()."),this.setFromProjectionMatrix(t)},iu.prototype.center=function(t){return console.warn("THREE.Line3: .center() has been renamed to .getCenter()."),this.getCenter(t)},gt.random16=function(){return console.warn("THREE.Math: .random16() has been deprecated. Use Math.random() instead."),Math.random()},gt.nearestPowerOfTwo=function(t){return console.warn("THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo()."),gt.floorPowerOfTwo(t)},gt.nextPowerOfTwo=function(t){return console.warn("THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo()."),gt.ceilPowerOfTwo(t)},_t.prototype.flattenToArrayOffset=function(t,e){return console.warn("THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."),this.toArray(t,e)},_t.prototype.multiplyVector3=function(t){return console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead."),t.applyMatrix3(this)},_t.prototype.multiplyVector3Array=function(){console.error("THREE.Matrix3: .multiplyVector3Array() has been removed.")},_t.prototype.applyToBufferAttribute=function(t){return console.warn("THREE.Matrix3: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix3( matrix ) instead."),t.applyMatrix3(this)},_t.prototype.applyToVector3Array=function(){console.error("THREE.Matrix3: .applyToVector3Array() has been removed.")},_t.prototype.getInverse=function(t){return console.warn("THREE.Matrix3: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."),this.copy(t).invert()},re.prototype.extractPosition=function(t){return console.warn("THREE.Matrix4: .extractPosition() has been renamed to .copyPosition()."),this.copyPosition(t)},re.prototype.flattenToArrayOffset=function(t,e){return console.warn("THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead."),this.toArray(t,e)},re.prototype.getPosition=function(){return console.warn("THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead."),(new Rt).setFromMatrixColumn(this,3)},re.prototype.setRotationFromQuaternion=function(t){return console.warn("THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion()."),this.makeRotationFromQuaternion(t)},re.prototype.multiplyToArray=function(){console.warn("THREE.Matrix4: .multiplyToArray() has been removed.")},re.prototype.multiplyVector3=function(t){return console.warn("THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead."),t.applyMatrix4(this)},re.prototype.multiplyVector4=function(t){return console.warn("THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead."),t.applyMatrix4(this)},re.prototype.multiplyVector3Array=function(){console.error("THREE.Matrix4: .multiplyVector3Array() has been removed.")},re.prototype.rotateAxis=function(t){console.warn("THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead."),t.transformDirection(this)},re.prototype.crossVector=function(t){return console.warn("THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead."),t.applyMatrix4(this)},re.prototype.translate=function(){console.error("THREE.Matrix4: .translate() has been removed.")},re.prototype.rotateX=function(){console.error("THREE.Matrix4: .rotateX() has been removed.")},re.prototype.rotateY=function(){console.error("THREE.Matrix4: .rotateY() has been removed.")},re.prototype.rotateZ=function(){console.error("THREE.Matrix4: .rotateZ() has been removed.")},re.prototype.rotateByAxis=function(){console.error("THREE.Matrix4: .rotateByAxis() has been removed.")},re.prototype.applyToBufferAttribute=function(t){return console.warn("THREE.Matrix4: .applyToBufferAttribute() has been removed. Use attribute.applyMatrix4( matrix ) instead."),t.applyMatrix4(this)},re.prototype.applyToVector3Array=function(){console.error("THREE.Matrix4: .applyToVector3Array() has been removed.")},re.prototype.makeFrustum=function(t,e,n,r,i,a){return console.warn("THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead."),this.makePerspective(t,e,r,n,i,a)},re.prototype.getInverse=function(t){return console.warn("THREE.Matrix4: .getInverse() has been removed. Use matrixInv.copy( matrix ).invert(); instead."),this.copy(t).invert()},De.prototype.isIntersectionLine=function(t){return console.warn("THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine()."),this.intersectsLine(t)},Lt.prototype.multiplyVector3=function(t){return console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."),t.applyQuaternion(this)},Lt.prototype.inverse=function(){return console.warn("THREE.Quaternion: .inverse() has been renamed to invert()."),this.invert()},ne.prototype.isIntersectionBox=function(t){return console.warn("THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox()."),this.intersectsBox(t)},ne.prototype.isIntersectionPlane=function(t){return console.warn("THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane()."),this.intersectsPlane(t)},ne.prototype.isIntersectionSphere=function(t){return console.warn("THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere()."),this.intersectsSphere(t)},Ve.prototype.area=function(){return console.warn("THREE.Triangle: .area() has been renamed to .getArea()."),this.getArea()},Ve.prototype.barycoordFromPoint=function(t,e){return console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."),this.getBarycoord(t,e)},Ve.prototype.midpoint=function(t){return console.warn("THREE.Triangle: .midpoint() has been renamed to .getMidpoint()."),this.getMidpoint(t)},Ve.prototypenormal=function(t){return console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."),this.getNormal(t)},Ve.prototype.plane=function(t){return console.warn("THREE.Triangle: .plane() has been renamed to .getPlane()."),this.getPlane(t)},Ve.barycoordFromPoint=function(t,e,n,r,i){return console.warn("THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord()."),Ve.getBarycoord(t,e,n,r,i)},Ve.normal=function(t,e,n,r){return console.warn("THREE.Triangle: .normal() has been renamed to .getNormal()."),Ve.getNormal(t,e,n,r)},Al.prototype.extractAllPoints=function(t){return console.warn("THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead."),this.extractPoints(t)},Al.prototype.extrude=function(t){return console.warn("THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead."),new as(this,t)},Al.prototype.makeGeometry=function(t){return console.warn("THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead."),new ds(this,t)},yt.prototype.fromAttribute=function(t,e,n){return console.warn("THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute()."),this.fromBufferAttribute(t,e,n)},yt.prototype.distanceToManhattan=function(t){return console.warn("THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."),this.manhattanDistanceTo(t)},yt.prototype.lengthManhattan=function(){return console.warn("THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength()."),this.manhattanLength()},Rt.prototype.setEulerFromRotationMatrix=function(){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")},Rt.prototype.setEulerFromQuaternion=function(){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},Rt.prototype.getPositionFromMatrix=function(t){return console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition()."),this.setFromMatrixPosition(t)},Rt.prototype.getScaleFromMatrix=function(t){return console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale()."),this.setFromMatrixScale(t)},Rt.prototype.getColumnFromMatrix=function(t,e){return console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn()."),this.setFromMatrixColumn(e,t)},Rt.prototype.applyProjection=function(t){return console.warn("THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead."),this.applyMatrix4(t)},Rt.prototype.fromAttribute=function(t,e,n){return console.warn("THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute()."),this.fromBufferAttribute(t,e,n)},Rt.prototype.distanceToManhattan=function(t){return console.warn("THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo()."),this.manhattanDistanceTo(t)},Rt.prototype.lengthManhattan=function(){return console.warn("THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength()."),this.manhattanLength()},Tt.prototype.fromAttribute=function(t,e,n){return console.warn("THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute()."),this.fromBufferAttribute(t,e,n)},Tt.prototype.lengthManhattan=function(){return console.warn("THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength()."),this.manhattanLength()},Le.prototype.getChildByName=function(t){return console.warn("THREE.Object3D: .getChildByName() has been renamed to .getObjectByName()."),this.getObjectByName(t)},Le.prototype.renderDepth=function(){console.warn("THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.")},Le.prototype.translate=function(t,e){return console.warn("THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead."),this.translateOnAxis(e,t)},Le.prototype.getWorldRotation=function(){console.error("THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.")},Le.prototype.applyMatrix=function(t){return console.warn("THREE.Object3D: .applyMatrix() has been renamed to .applyMatrix4()."),this.applyMatrix4(t)},Object.defineProperties(Le.prototype,{eulerOrder:{get:function(){return console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order},set:function(t){console.warn("THREE.Object3D: .eulerOrder is now .rotation.order."),this.rotation.order=t}},useQuaternion:{get:function(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")},set:function(){console.warn("THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.")}}}),kn.prototype.setDrawMode=function(){console.error("THREE.Mesh: .setDrawMode() has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.")},Object.defineProperties(kn.prototype,{drawMode:{get:function(){return console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode."),0},set:function(){console.error("THREE.Mesh: .drawMode has been removed. The renderer now always assumes THREE.TrianglesDrawMode. Transform your geometry via BufferGeometryUtils.toTrianglesDrawMode() if necessary.")}}}),Object.defineProperties(Oa.prototype,{objects:{get:function(){return console.warn("THREE.LOD: .objects has been renamed to .levels."),this.levels}}}),Object.defineProperty(qa.prototype,"useVertexTexture",{get:function(){console.warn("THREE.Skeleton: useVertexTexture has been removed.")},set:function(){console.warn("THREE.Skeleton: useVertexTexture has been removed.")}}),ka.prototype.initBones=function(){console.error("THREE.SkinnedMesh: initBones() has been removed.")},Object.defineProperty(al.prototype,"__arcLengthDivisions",{get:function(){return console.warn("THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions."),this.arcLengthDivisions},set:function(t){console.warn("THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions."),this.arcLengthDivisions=t}}),Jn.prototype.setLens=function(t,e){console.warn("THREE.PerspectiveCamera.setLens is deprecated. Use .setFocalLength and .filmGauge for a photographic setup."),void 0!==e&&(this.filmGauge=e),this.setFocalLength(t)},Object.defineProperties(Ll.prototype,{onlyShadow:{set:function(){console.warn("THREE.Light: .onlyShadow has been removed.")}},shadowCameraFov:{set:function(t){console.warn("THREE.Light: .shadowCameraFov is now .shadow.camera.fov."),this.shadow.camera.fov=t}},shadowCameraLeft:{set:function(t){console.warn("THREE.Light: .shadowCameraLeft is now .shadow.camera.left."),this.shadow.camera.left=t}},shadowCameraRight:{set:function(t){console.warn("THREE.Light: .shadowCameraRight is now .shadow.camera.right."),this.shadow.camera.right=t}},shadowCameraTop:{set:function(t){console.warn("THREE.Light: .shadowCameraTop is now .shadow.camera.top."),this.shadow.camera.top=t}},shadowCameraBottom:{set:function(t){console.warn("THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom."),this.shadow.camera.bottom=t}},shadowCameraNear:{set:function(t){console.warn("THREE.Light: .shadowCameraNear is now .shadow.camera.near."),this.shadow.camera.near=t}},shadowCameraFar:{set:function(t){console.warn("THREE.Light: .shadowCameraFar is now .shadow.camera.far."),this.shadow.camera.far=t}},shadowCameraVisible:{set:function(){console.warn("THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.")}},shadowBias:{set:function(t){console.warn("THREE.Light: .shadowBias is now .shadow.bias."),this.shadow.bias=t}},shadowDarkness:{set:function(){console.warn("THREE.Light: .shadowDarkness has been removed.")}},shadowMapWidth:{set:function(t){console.warn("THREE.Light: .shadowMapWidth is now .shadow.mapSize.width."),this.shadow.mapSize.width=t}},shadowMapHeight:{set:function(t){console.warn("THREE.Light: .shadowMapHeight is now .shadow.mapSize.height."),this.shadow.mapSize.height=t}}}),Object.defineProperties(nn.prototype,{length:{get:function(){return console.warn("THREE.BufferAttribute: .length has been deprecated. Use .count instead."),this.array.length}},dynamic:{get:function(){return console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."),this.usage===rt},set:function(){console.warn("THREE.BufferAttribute: .dynamic has been deprecated. Use .usage instead."),this.setUsage(rt)}}}),nn.prototype.setDynamic=function(t){return console.warn("THREE.BufferAttribute: .setDynamic() has been deprecated. Use .setUsage() instead."),this.setUsage(!0===t?rt:nt),this},nn.prototype.copyIndicesArray=function(){console.error("THREE.BufferAttribute: .copyIndicesArray() has been removed.")},nn.prototype.setArray=function(){console.error("THREE.BufferAttribute: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers")},Sn.prototype.addIndex=function(t){console.warn("THREE.BufferGeometry: .addIndex() has been renamed to .setIndex()."),this.setIndex(t)},Sn.prototype.addAttribute=function(t,e){return console.warn("THREE.BufferGeometry: .addAttribute() has been renamed to .setAttribute()."),e&&e.isBufferAttribute||e&&e.isInterleavedBufferAttribute?"index"===t?(console.warn("THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute."),this.setIndex(e),this):this.setAttribute(t,e):(console.warn("THREE.BufferGeometry: .addAttribute() now expects ( name, attribute )."),this.setAttribute(t,new nn(arguments[1],arguments[2])))},Sn.prototype.addDrawCall=function(t,e,n){void 0!==n&&console.warn("THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset."),console.warn("THREE.BufferGeometry: .addDrawCall() is now .addGroup()."),this.addGroup(t,e)},Sn.prototype.clearDrawCalls=function(){console.warn("THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups()."),this.clearGroups()},Sn.prototype.computeOffsets=function(){console.warn("THREE.BufferGeometry: .computeOffsets() has been removed.")},Sn.prototype.removeAttribute=function(t){return console.warn("THREE.BufferGeometry: .removeAttribute() has been renamed to .deleteAttribute()."),this.deleteAttribute(t)},Sn.prototype.applyMatrix=function(t){return console.warn("THREE.BufferGeometry: .applyMatrix() has been renamed to .applyMatrix4()."),this.applyMatrix4(t)},Object.defineProperties(Sn.prototype,{drawcalls:{get:function(){return console.error("THREE.BufferGeometry: .drawcalls has been renamed to .groups."),this.groups}},offsets:{get:function(){return console.warn("THREE.BufferGeometry: .offsets has been renamed to .groups."),this.groups}}}),Object.defineProperties(Jl.prototype,{maxInstancedCount:{get:function(){return console.warn("THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount."),this.instanceCount},set:function(t){console.warn("THREE.InstancedBufferGeometry: .maxInstancedCount has been renamed to .instanceCount."),this.instanceCount=t}}}),Object.defineProperties(Zc.prototype,{linePrecision:{get:function(){return console.warn("THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead."),this.params.Line.threshold},set:function(t){console.warn("THREE.Raycaster: .linePrecision has been deprecated. Use .params.Line.threshold instead."),this.params.Line.threshold=t}}}),Object.defineProperties(ma.prototype,{dynamic:{get:function(){return console.warn("THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead."),this.usage===rt},set:function(t){console.warn("THREE.InterleavedBuffer: .length has been deprecated. Use .usage instead."),this.setUsage(t)}}}),ma.prototype.setDynamic=function(t){return console.warn("THREE.InterleavedBuffer: .setDynamic() has been deprecated. Use .setUsage() instead."),this.setUsage(!0===t?rt:nt),this},ma.prototype.setArray=function(){console.error("THREE.InterleavedBuffer: .setArray has been removed. Use BufferGeometry .setAttribute to replace/resize attribute buffers")},as.prototype.getArrays=function(){console.error("THREE.ExtrudeGeometry: .getArrays() has been removed.")},as.prototype.addShapeList=function(){console.error("THREE.ExtrudeGeometry: .addShapeList() has been removed.")},as.prototype.addShape=function(){console.error("THREE.ExtrudeGeometry: .addShape() has been removed.")},fa.prototype.dispose=function(){console.error("THREE.Scene: .dispose() has been removed.")},Object.defineProperties(qc.prototype,{dynamic:{set:function(){console.warn("THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.")}},onUpdate:{value:function(){return console.warn("THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead."),this}}}),Object.defineProperties(je.prototype,{wrapAround:{get:function(){console.warn("THREE.Material: .wrapAround has been removed.")},set:function(){console.warn("THREE.Material: .wrapAround has been removed.")}},overdraw:{get:function(){console.warn("THREE.Material: .overdraw has been removed.")},set:function(){console.warn("THREE.Material: .overdraw has been removed.")}},wrapRGB:{get:function(){return console.warn("THREE.Material: .wrapRGB has been removed."),new Ke}},shading:{get:function(){console.error("THREE."+this.type+": .shading has been removed. Use the boolean .flatShading instead.")},set:function(t){console.warn("THREE."+this.type+": .shading has been removed. Use the boolean .flatShading instead."),this.flatShading=1===t}},stencilMask:{get:function(){return console.warn("THREE."+this.type+": .stencilMask has been removed. Use .stencilFuncMask instead."),this.stencilFuncMask},set:function(t){console.warn("THREE."+this.type+": .stencilMask has been removed. Use .stencilFuncMask instead."),this.stencilFuncMask=t}}}),Object.defineProperties(Ts.prototype,{metal:{get:function(){return console.warn("THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead."),!1},set:function(){console.warn("THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead")}}}),Object.defineProperties(Ss.prototype,{transparency:{get:function(){return console.warn("THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission."),this.transmission},set:function(t){console.warn("THREE.MeshPhysicalMaterial: .transparency has been renamed to .transmission."),this.transmission=t}}}),Object.defineProperties(Yn.prototype,{derivatives:{get:function(){return console.warn("THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives."),this.extensions.derivatives},set:function(t){console.warn("THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives."),this.extensions.derivatives=t}}}),ua.prototype.clearTarget=function(t,e,n,r){console.warn("THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead."),this.setRenderTarget(t),this.clear(e,n,r)},ua.prototype.animate=function(t){console.warn("THREE.WebGLRenderer: .animate() is now .setAnimationLoop()."),this.setAnimationLoop(t)},ua.prototype.getCurrentRenderTarget=function(){return console.warn("THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget()."),this.getRenderTarget()},ua.prototype.getMaxAnisotropy=function(){return console.warn("THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy()."),this.capabilities.getMaxAnisotropy()},ua.prototype.getPrecision=function(){return console.warn("THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision."),this.capabilities.precision},ua.prototype.resetGLState=function(){return console.warn("THREE.WebGLRenderer: .resetGLState() is now .state.reset()."),this.state.reset()},ua.prototype.supportsFloatTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( 'OES_texture_float' )."),this.extensions.get("OES_texture_float")},ua.prototype.supportsHalfFloatTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( 'OES_texture_half_float' )."),this.extensions.get("OES_texture_half_float")},ua.prototype.supportsStandardDerivatives=function(){return console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( 'OES_standard_derivatives' )."),this.extensions.get("OES_standard_derivatives")},ua.prototype.supportsCompressedTextureS3TC=function(){return console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( 'WEBGL_compressed_texture_s3tc' )."),this.extensions.get("WEBGL_compressed_texture_s3tc")},ua.prototype.supportsCompressedTexturePVRTC=function(){return console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( 'WEBGL_compressed_texture_pvrtc' )."),this.extensions.get("WEBGL_compressed_texture_pvrtc")},ua.prototype.supportsBlendMinMax=function(){return console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( 'EXT_blend_minmax' )."),this.extensions.get("EXT_blend_minmax")},ua.prototype.supportsVertexTextures=function(){return console.warn("THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures."),this.capabilities.vertexTextures},ua.prototype.supportsInstancedArrays=function(){return console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( 'ANGLE_instanced_arrays' )."),this.extensions.get("ANGLE_instanced_arrays")},ua.prototype.enableScissorTest=function(t){console.warn("THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest()."),this.setScissorTest(t)},ua.prototype.initMaterial=function(){console.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")},ua.prototype.addPrePlugin=function(){console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")},ua.prototype.addPostPlugin=function(){console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")},ua.prototype.updateShadowMap=function(){console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")},ua.prototype.setFaceCulling=function(){console.warn("THREE.WebGLRenderer: .setFaceCulling() has been removed.")},ua.prototype.allocTextureUnit=function(){console.warn("THREE.WebGLRenderer: .allocTextureUnit() has been removed.")},ua.prototype.setTexture=function(){console.warn("THREE.WebGLRenderer: .setTexture() has been removed.")},ua.prototype.setTexture2D=function(){console.warn("THREE.WebGLRenderer: .setTexture2D() has been removed.")},ua.prototype.setTextureCube=function(){console.warn("THREE.WebGLRenderer: .setTextureCube() has been removed.")},ua.prototype.getActiveMipMapLevel=function(){return console.warn("THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel()."),this.getActiveMipmapLevel()},Object.defineProperties(ua.prototype,{shadowMapEnabled:{get:function(){return this.shadowMap.enabled},set:function(t){console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled."),this.shadowMap.enabled=t}},shadowMapType:{get:function(){return this.shadowMap.type},set:function(t){console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type."),this.shadowMap.type=t}},shadowMapCullFace:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.")},set:function(){console.warn("THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.")}},context:{get:function(){return console.warn("THREE.WebGLRenderer: .context has been removed. Use .getContext() instead."),this.getContext()}},vr:{get:function(){return console.warn("THREE.WebGLRenderer: .vr has been renamed to .xr"),this.xr}},gammaInput:{get:function(){return console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead."),!1},set:function(){console.warn("THREE.WebGLRenderer: .gammaInput has been removed. Set the encoding for textures via Texture.encoding instead.")}},gammaOutput:{get:function(){return console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."),!1},set:function(t){console.warn("THREE.WebGLRenderer: .gammaOutput has been removed. Set WebGLRenderer.outputEncoding instead."),this.outputEncoding=!0===t?Z:Y}},toneMappingWhitePoint:{get:function(){return console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed."),1},set:function(){console.warn("THREE.WebGLRenderer: .toneMappingWhitePoint has been removed.")}}}),Object.defineProperties(ea.prototype,{cullFace:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.")},set:function(){console.warn("THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.")}},renderReverseSided:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.")},set:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.")}},renderSingleSided:{get:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.")},set:function(){console.warn("THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.")}}}),Object.defineProperties(Et.prototype,{wrapS:{get:function(){return console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."),this.texture.wrapS},set:function(t){console.warn("THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS."),this.texture.wrapS=t}},wrapT:{get:function(){return console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."),this.texture.wrapT},set:function(t){console.warn("THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT."),this.texture.wrapT=t}},magFilter:{get:function(){return console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."),this.texture.magFilter},set:function(t){console.warn("THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter."),this.texture.magFilter=t}},minFilter:{get:function(){return console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."),this.texture.minFilter},set:function(t){console.warn("THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter."),this.texture.minFilter=t}},anisotropy:{get:function(){return console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."),this.texture.anisotropy},set:function(t){console.warn("THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy."),this.texture.anisotropy=t}},offset:{get:function(){return console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."),this.texture.offset},set:function(t){console.warn("THREE.WebGLRenderTarget: .offset is now .texture.offset."),this.texture.offset=t}},repeat:{get:function(){return console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."),this.texture.repeat},set:function(t){console.warn("THREE.WebGLRenderTarget: .repeat is now .texture.repeat."),this.texture.repeat=t}},format:{get:function(){return console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."),this.texture.format},set:function(t){console.warn("THREE.WebGLRenderTarget: .format is now .texture.format."),this.texture.format=t}},type:{get:function(){return console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."),this.texture.type},set:function(t){console.warn("THREE.WebGLRenderTarget: .type is now .texture.type."),this.texture.type=t}},generateMipmaps:{get:function(){return console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."),this.texture.generateMipmaps},set:function(t){console.warn("THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps."),this.texture.generateMipmaps=t}}}),Object.defineProperties(Mc.prototype,{load:{value:function(t){console.warn("THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.");var e=this;return(new uc).load(t,(function(t){e.setBuffer(t)})),this}},startTime:{set:function(){console.warn("THREE.Audio: .startTime is now .play( delay ).")}}}),Rc.prototype.getData=function(){return console.warn("THREE.AudioAnalyser: .getData() is now .getFrequencyData()."),this.getFrequencyData()},Kn.prototype.updateCubeMap=function(t,e){return console.warn("THREE.CubeCamera: .updateCubeMap() is now .update()."),this.update(t,e)},Kn.prototype.clear=function(t,e,n,r){return console.warn("THREE.CubeCamera: .clear() is now .renderTarget.clear()."),this.renderTarget.clear(t,e,n,r)},bt.crossOrigin=void 0,bt.loadTexture=function(t,e,n,r){console.warn("THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.");var i=new il;i.setCrossOrigin(this.crossOrigin);var a=i.load(t,n,void 0,r);return e&&(a.mapping=e),a},bt.loadTextureCube=function(t,e,n,r){console.warn("THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.");var i=new nl;i.setCrossOrigin(this.crossOrigin);var a=i.load(t,n,void 0,r);return e&&(a.mapping=e),a},bt.loadCompressedTexture=function(){console.error("THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.")},bt.loadCompressedTextureCube=function(){console.error("THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.")};var dh={createMultiMaterialObject:function(){console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js")},detach:function(){console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js")},attach:function(){console.error("THREE.SceneUtils has been moved to /examples/jsm/utils/SceneUtils.js")}};"undefined"!=typeof __THREE_DEVTOOLS__&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("register",{detail:{revision:n}})),"undefined"!=typeof window&&(window.__THREE__?console.warn("WARNING: Multiple instances of Three.js being imported."):window.__THREE__=n),t.ACESFilmicToneMapping=4,t.AddEquation=r,t.AddOperation=2,t.AdditiveAnimationBlendMode=X,t.AdditiveBlending=2,t.AlphaFormat=1021,t.AlwaysDepth=1,t.AlwaysStencilFunc=519,t.AmbientLight=Wl,t.AmbientLightProbe=dc,t.AnimationClip=js,t.AnimationLoader=$s,t.AnimationMixer=jc,t.AnimationObjectGroup=Vc,t.AnimationUtils=Ds,t.ArcCurve=sl,t.ArrayCamera=aa,t.ArrowHelper=Ou,t.Audio=Mc,t.AudioAnalyser=Rc,t.AudioContext=cc,t.AudioListener=wc,t.AudioLoader=uc,t.AxesHelper=zu,t.AxisHelper=function(t){return console.warn("THREE.AxisHelper has been renamed to THREE.AxesHelper."),new zu(t)},t.BackSide=1,t.BasicDepthPacking=3200,t.BasicShadowMap=0,t.BinaryTextureLoader=function(t){return console.warn("THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader."),new rl(t)},t.Bone=Va,t.BooleanKeyframeTrack=Fs,t.BoundingBoxHelper=function(t,e){return console.warn("THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead."),new Du(t,e)},t.Box2=eu,t.Box3=Dt,t.Box3Helper=Iu,t.BoxBufferGeometry=Wn,t.BoxGeometry=Wn,t.BoxHelper=Du,t.BufferAttribute=nn,t.BufferGeometry=Sn,t.BufferGeometryLoader=Kl,t.ByteType=1010,t.Cache=Xs,t.Camera=Zn,t.CameraHelper=Eu,t.CanvasRenderer=function(){console.error("THREE.CanvasRenderer has been removed")},t.CanvasTexture=xo,t.CatmullRomCurve3=pl,t.CineonToneMapping=3,t.CircleBufferGeometry=bo,t.CircleGeometry=bo,t.ClampToEdgeWrapping=d,t.Clock=vc,t.Color=Ke,t.ColorKeyframeTrack=Hs,t.CompressedTexture=yo,t.CompressedTextureLoader=tl,t.ConeBufferGeometry=Mo,t.ConeGeometry=Mo,t.CubeCamera=Kn,t.CubeReflectionMapping=a,t.CubeRefractionMapping=o,t.CubeTexture=$n,t.CubeTextureLoader=nl,t.CubeUVReflectionMapping=c,t.CubeUVRefractionMapping=u,t.CubicBezierCurve=gl,t.CubicBezierCurve3=yl,t.CubicInterpolant=Ns,t.CullFaceBack=1,t.CullFaceFront=2,t.CullFaceFrontBack=3,t.CullFaceNone=0,t.Curve=al,t.CurvePath=Tl,t.CustomBlending=5,t.CustomToneMapping=5,t.CylinderBufferGeometry=wo,t.CylinderGeometry=wo,t.Cylindrical=$c,t.DataTexture=er,t.DataTexture2DArray=Tr,t.DataTexture3D=Er,t.DataTextureLoader=rl,t.DataUtils=Gu,t.DecrementStencilOp=7683,t.DecrementWrapStencilOp=34056,t.DefaultLoadingManager=Zs,t.DepthFormat=L,t.DepthStencilFormat=R,t.DepthTexture=_o,t.DirectionalLight=Vl,t.DirectionalLightHelper=Mu,t.DiscreteInterpolant=Os,t.DodecahedronBufferGeometry=To,t.DodecahedronGeometry=To,t.DoubleSide=2,t.DstAlphaFactor=206,t.DstColorFactor=208,t.DynamicBufferAttribute=function(t,e){return console.warn("THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setUsage( THREE.DynamicDrawUsage ) instead."),new nn(t,e).setUsage(rt)},t.DynamicCopyUsage=35050,t.DynamicDrawUsage=rt,t.DynamicReadUsage=35049,t.EdgesGeometry=Co,t.EdgesHelper=function(t,e){return console.warn("THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead."),new so(new Co(t.geometry),new Ka({color:void 0!==e?e:16777215}))},t.EllipseCurve=ol,t.EqualDepth=4,t.EqualStencilFunc=514,t.EquirectangularReflectionMapping=s,t.EquirectangularRefractionMapping=l,t.Euler=pe,t.EventDispatcher=pt,t.ExtrudeBufferGeometry=as,t.ExtrudeGeometry=as,t.FaceColors=1,t.FileLoader=Ks,t.FlatShading=1,t.Float16BufferAttribute=hn,t.Float32Attribute=function(t,e){return console.warn("THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead."),new dn(t,e)},t.Float32BufferAttribute=dn,t.Float64Attribute=function(t,e){return console.warn("THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead."),new pn(t,e)},t.Float64BufferAttribute=pn,t.FloatType=M,t.Fog=pa,t.FogExp2=da,t.Font=ac,t.FontLoader=lc,t.FrontSide=0,t.Frustum=ir,t.GLBufferAttribute=Yc,t.GLSL1="100",t.GLSL3=it,t.GammaEncoding=J,t.GreaterDepth=6,t.GreaterEqualDepth=5,t.GreaterEqualStencilFunc=518,t.GreaterStencilFunc=516,t.GridHelper=yu,t.Group=oa,t.HalfFloatType=S,t.HemisphereLight=Rl,t.HemisphereLightHelper=gu,t.HemisphereLightProbe=hc,t.IcosahedronBufferGeometry=ss,t.IcosahedronGeometry=ss,t.ImageBitmapLoader=rc,t.ImageLoader=el,t.ImageUtils=bt,t.ImmediateRenderObject=au,t.IncrementStencilOp=7682,t.IncrementWrapStencilOp=34055,t.InstancedBufferAttribute=Ql,t.InstancedBufferGeometry=Jl,t.InstancedInterleavedBuffer=Xc,t.InstancedMesh=Qa,t.Int16Attribute=function(t,e){return console.warn("THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead."),new sn(t,e)},t.Int16BufferAttribute=sn,t.Int32Attribute=function(t,e){return console.warn("THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead."),new cn(t,e)},t.Int32BufferAttribute=cn,t.Int8Attribute=function(t,e){return console.warn("THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead."),new rn(t,e)},t.Int8BufferAttribute=rn,t.IntType=1013,t.InterleavedBuffer=ma,t.InterleavedBufferAttribute=ga,t.Interpolant=Is,t.InterpolateDiscrete=G,t.InterpolateLinear=U,t.InterpolateSmooth=k,t.InvertStencilOp=5386,t.JSONLoader=function(){console.error("THREE.JSONLoader has been removed.")},t.KeepStencilOp=et,t.KeyframeTrack=zs,t.LOD=Oa,t.LatheBufferGeometry=ls,t.LatheGeometry=ls,t.Layers=fe,t.LensFlare=function(){console.error("THREE.LensFlare has been moved to /examples/jsm/objects/Lensflare.js")},t.LessDepth=2,t.LessEqualDepth=3,t.LessEqualStencilFunc=515,t.LessStencilFunc=513,t.Light=Ll,t.LightProbe=Xl,t.Line=io,t.Line3=iu,t.LineBasicMaterial=Ka,t.LineCurve=xl,t.LineCurve3=_l,t.LineDashedMaterial=Cs,t.LineLoop=lo,t.LinePieces=1,t.LineSegments=so,t.LineStrip=0,t.LinearEncoding=Y,t.LinearFilter=g,t.LinearInterpolant=Bs,t.LinearMipMapLinearFilter=1008,t.LinearMipMapNearestFilter=1007,t.LinearMipmapLinearFilter=x,t.LinearMipmapNearestFilter=y,t.LinearToneMapping=1,t.Loader=Js,t.LoaderUtils=Zl,t.LoadingManager=Ys,t.LogLuvEncoding=3003,t.LoopOnce=2200,t.LoopPingPong=2202,t.LoopRepeat=2201,t.LuminanceAlphaFormat=1025,t.LuminanceFormat=1024,t.MOUSE={LEFT:0,MIDDLE:1,RIGHT:2,ROTATE:0,DOLLY:1,PAN:2},t.Material=je,t.MaterialLoader=Yl,t.Math=gt,t.MathUtils=gt,t.Matrix3=_t,t.Matrix4=re,t.MaxEquation=104,t.Mesh=kn,t.MeshBasicMaterial=$e,t.MeshDepthMaterial=$i,t.MeshDistanceMaterial=ta,t.MeshFaceMaterial=function(t){return console.warn("THREE.MeshFaceMaterial has been removed. Use an Array instead."),t},t.MeshLambertMaterial=Ls,t.MeshMatcapMaterial=Rs,t.MeshNormalMaterial=As,t.MeshPhongMaterial=Ts,t.MeshPhysicalMaterial=Ss,t.MeshStandardMaterial=Ms,t.MeshToonMaterial=Es,t.MinEquation=103,t.MirroredRepeatWrapping=p,t.MixOperation=1,t.MultiMaterial=function(t){return void 0===t&&(t=[]),console.warn("THREE.MultiMaterial has been removed. Use an Array instead."),t.isMultiMaterial=!0,t.materials=t,t.clone=function(){return t.slice()},t},t.MultiplyBlending=4,t.MultiplyOperation=0,t.NearestFilter=f,t.NearestMipMapLinearFilter=1005,t.NearestMipMapNearestFilter=1004,t.NearestMipmapLinearFilter=v,t.NearestMipmapNearestFilter=m,t.NeverDepth=0,t.NeverStencilFunc=512,t.NoBlending=0,t.NoColors=0,t.NoToneMapping=0,t.NormalAnimationBlendMode=q,t.NormalBlending=1,t.NotEqualDepth=7,t.NotEqualStencilFunc=517,t.NumberKeyframeTrack=Gs,t.Object3D=Le,t.ObjectLoader=$l,t.ObjectSpaceNormalMap=1,t.OctahedronBufferGeometry=cs,t.OctahedronGeometry=cs,t.OneFactor=201,t.OneMinusDstAlphaFactor=207,t.OneMinusDstColorFactor=209,t.OneMinusSrcAlphaFactor=205,t.OneMinusSrcColorFactor=203,t.OrthographicCamera=Ul,t.PCFShadowMap=1,t.PCFSoftShadowMap=2,t.PMREMGenerator=ah,t.ParametricBufferGeometry=us,t.ParametricGeometry=us,t.Particle=function(t){return console.warn("THREE.Particle has been renamed to THREE.Sprite."),new Da(t)},t.ParticleBasicMaterial=function(t){return console.warn("THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial."),new co(t)},t.ParticleSystem=function(t,e){return console.warn("THREE.ParticleSystem has been renamed to THREE.Points."),new mo(t,e)},t.ParticleSystemMaterial=function(t){return console.warn("THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial."),new co(t)},t.Path=El,t.PerspectiveCamera=Jn,t.Plane=De,t.PlaneBufferGeometry=sr,t.PlaneGeometry=sr,t.PlaneHelper=Nu,t.PointCloud=function(t,e){return console.warn("THREE.PointCloud has been renamed to THREE.Points."),new mo(t,e)},t.PointCloudMaterial=function(t){return console.warn("THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial."),new co(t)},t.PointLight=Gl,t.PointLightHelper=pu,t.Points=mo,t.PointsMaterial=co,t.PolarGridHelper=xu,t.PolyhedronBufferGeometry=So,t.PolyhedronGeometry=So,t.PositionalAudio=Lc,t.PropertyBinding=kc,t.PropertyMixer=Cc,t.QuadraticBezierCurve=bl,t.QuadraticBezierCurve3=wl,t.Quaternion=Lt,t.QuaternionKeyframeTrack=ks,t.QuaternionLinearInterpolant=Us,t.REVISION=n,t.RGBADepthPacking=3201,t.RGBAFormat=A,t.RGBAIntegerFormat=1033,t.RGBA_ASTC_10x10_Format=37819,t.RGBA_ASTC_10x5_Format=37816,t.RGBA_ASTC_10x6_Format=37817,t.RGBA_ASTC_10x8_Format=37818,t.RGBA_ASTC_12x10_Format=37820,t.RGBA_ASTC_12x12_Format=37821,t.RGBA_ASTC_4x4_Format=37808,t.RGBA_ASTC_5x4_Format=37809,t.RGBA_ASTC_5x5_Format=37810,t.RGBA_ASTC_6x5_Format=37811,t.RGBA_ASTC_6x6_Format=37812,t.RGBA_ASTC_8x5_Format=37813,t.RGBA_ASTC_8x6_Format=37814,t.RGBA_ASTC_8x8_Format=37815,t.RGBA_BPTC_Format=36492,t.RGBA_ETC2_EAC_Format=H,t.RGBA_PVRTC_2BPPV1_Format=z,t.RGBA_PVRTC_4BPPV1_Format=O,t.RGBA_S3TC_DXT1_Format=P,t.RGBA_S3TC_DXT3_Format=D,t.RGBA_S3TC_DXT5_Format=I,t.RGBDEncoding=tt,t.RGBEEncoding=Q,t.RGBEFormat=1023,t.RGBFormat=E,t.RGBIntegerFormat=1032,t.RGBM16Encoding=$,t.RGBM7Encoding=K,t.RGB_ETC1_Format=36196,t.RGB_ETC2_Format=F,t.RGB_PVRTC_2BPPV1_Format=B,t.RGB_PVRTC_4BPPV1_Format=N,t.RGB_S3TC_DXT1_Format=C,t.RGFormat=1030,t.RGIntegerFormat=1031,t.RawShaderMaterial=ws,t.Ray=ne,t.Raycaster=Zc,t.RectAreaLight=jl,t.RedFormat=1028,t.RedIntegerFormat=1029,t.ReinhardToneMapping=2,t.RepeatWrapping=h,t.ReplaceStencilOp=7681,t.ReverseSubtractEquation=102,t.RingBufferGeometry=hs,t.RingGeometry=hs,t.SRGB8_ALPHA8_ASTC_10x10_Format=37851,t.SRGB8_ALPHA8_ASTC_10x5_Format=37848,t.SRGB8_ALPHA8_ASTC_10x6_Format=37849,t.SRGB8_ALPHA8_ASTC_10x8_Format=37850,t.SRGB8_ALPHA8_ASTC_12x10_Format=37852,t.SRGB8_ALPHA8_ASTC_12x12_Format=37853,t.SRGB8_ALPHA8_ASTC_4x4_Format=37840,t.SRGB8_ALPHA8_ASTC_5x4_Format=37841,t.SRGB8_ALPHA8_ASTC_5x5_Format=37842,t.SRGB8_ALPHA8_ASTC_6x5_Format=37843,t.SRGB8_ALPHA8_ASTC_6x6_Format=37844,t.SRGB8_ALPHA8_ASTC_8x5_Format=37845,t.SRGB8_ALPHA8_ASTC_8x6_Format=37846,t.SRGB8_ALPHA8_ASTC_8x8_Format=37847,t.Scene=fa,t.SceneUtils=dh,t.ShaderChunk=lr,t.ShaderLib=ur,t.ShaderMaterial=Yn,t.ShadowMaterial=bs,t.Shape=Al,t.ShapeBufferGeometry=ds,t.ShapeGeometry=ds,t.ShapePath=ic,t.ShapeUtils=ns,t.ShortType=1011,t.Skeleton=qa,t.SkeletonHelper=hu,t.SkinnedMesh=ka,t.SmoothShading=2,t.Sphere=Yt,t.SphereBufferGeometry=ps,t.SphereGeometry=ps,t.Spherical=Kc,t.SphericalHarmonics3=ql,t.SplineCurve=Ml,t.SpotLight=Bl,t.SpotLightHelper=su,t.Sprite=Da,t.SpriteMaterial=xa,t.SrcAlphaFactor=204,t.SrcAlphaSaturateFactor=210,t.SrcColorFactor=202,t.StaticCopyUsage=35046,t.StaticDrawUsage=nt,t.StaticReadUsage=35045,t.StereoCamera=mc,t.StreamCopyUsage=35042,t.StreamDrawUsage=35040,t.StreamReadUsage=35041,t.StringKeyframeTrack=Vs,t.SubtractEquation=101,t.SubtractiveBlending=3,t.TOUCH={ROTATE:0,PAN:1,DOLLY_PAN:2,DOLLY_ROTATE:3},t.TangentSpaceNormalMap=0,t.TetrahedronBufferGeometry=fs,t.TetrahedronGeometry=fs,t.TextBufferGeometry=ms,t.TextGeometry=ms,t.Texture=Mt,t.TextureLoader=il,t.TorusBufferGeometry=vs,t.TorusGeometry=vs,t.TorusKnotBufferGeometry=gs,t.TorusKnotGeometry=gs,t.Triangle=Ve,t.TriangleFanDrawMode=2,t.TriangleStripDrawMode=1,t.TrianglesDrawMode=0,t.TubeBufferGeometry=ys,t.TubeGeometry=ys,t.UVMapping=i,t.Uint16Attribute=function(t,e){return console.warn("THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead."),new ln(t,e)},t.Uint16BufferAttribute=ln,t.Uint32Attribute=function(t,e){return console.warn("THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead."),new un(t,e)},t.Uint32BufferAttribute=un,t.Uint8Attribute=function(t,e){return console.warn("THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead."),new an(t,e)},t.Uint8BufferAttribute=an,t.Uint8ClampedAttribute=function(t,e){return console.warn("THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead."),new on(t,e)},t.Uint8ClampedBufferAttribute=on,t.Uniform=qc,t.UniformsLib=cr,t.UniformsUtils=Xn,t.UnsignedByteType=_,t.UnsignedInt248Type=T,t.UnsignedIntType=w,t.UnsignedShort4444Type=1017,t.UnsignedShort5551Type=1018,t.UnsignedShort565Type=1019,t.UnsignedShortType=b,t.VSMShadowMap=3,t.Vector2=yt,t.Vector3=Rt,t.Vector4=Tt,t.VectorKeyframeTrack=Ws,t.Vertex=function(t,e,n){return console.warn("THREE.Vertex has been removed. Use THREE.Vector3 instead."),new Rt(t,e,n)},t.VertexColors=2,t.VideoTexture=go,t.WebGL1Renderer=ha,t.WebGLCubeRenderTarget=tr,t.WebGLMultisampleRenderTarget=At,t.WebGLRenderTarget=Et,t.WebGLRenderTargetCube=function(t,e,n){return console.warn("THREE.WebGLRenderTargetCube( width, height, options ) is now WebGLCubeRenderTarget( size, options )."),new tr(t,n)},t.WebGLRenderer=ua,t.WebGLUtils=ia,t.WireframeGeometry=xs,t.WireframeHelper=function(t,e){return console.warn("THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead."),new so(new xs(t.geometry),new Ka({color:void 0!==e?e:16777215}))},t.WrapAroundEnding=j,t.XHRLoader=function(t){return console.warn("THREE.XHRLoader has been renamed to THREE.FileLoader."),new Ks(t)},t.ZeroCurvatureEnding=V,t.ZeroFactor=200,t.ZeroSlopeEnding=W,t.ZeroStencilOp=0,t.sRGBEncoding=Z,Object.defineProperty(t,"__esModule",{value:!0})})); diff --git a/package.json b/package.json new file mode 100644 index 0000000..99e701b --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "online-3d-viewer", + "description": "Online 3D Viewer", + "version": "0.7.0", + "repository": "github:kovacsv/Online3DViewer", + "license": "MIT", + "devDependencies": { + "google-closure-compiler": "^20210302.0.0", + "jshint": "^2.12.0", + "mocha": "^8.3.2", + "svgo": "^2.2.2" + }, + "scripts": { + "test": "mocha test", + "build": "python tools/create_package.py", + "update": "python tools/update_includes.py", + "svg": "python tools/optimize_svg_files.py" + } +} diff --git a/source/core/core.js b/source/core/core.js new file mode 100644 index 0000000..1fd1ef4 --- /dev/null +++ b/source/core/core.js @@ -0,0 +1,4 @@ +OV = +{ + +}; diff --git a/source/core/taskrunner.js b/source/core/taskrunner.js new file mode 100644 index 0000000..e023168 --- /dev/null +++ b/source/core/taskrunner.js @@ -0,0 +1,41 @@ +OV.TaskRunner = class +{ + constructor () + { + this.count = null; + this.current = null; + this.callbacks = null; + } + + Run (count, callbacks) + { + this.count = count; + this.current = 0; + this.callbacks = callbacks; + if (count === 0) { + this.TaskReady (); + } else { + this.RunOnce (); + } + } + + RunOnce () + { + let obj = this; + setTimeout (function () { + obj.callbacks.runTask (obj.current, obj.TaskReady.bind (obj)); + }, 0); + } + + TaskReady () + { + this.current += 1; + if (this.current < this.count) { + this.RunOnce (); + } else { + if (this.callbacks.onReady) { + this.callbacks.onReady (); + } + } + } +}; diff --git a/source/export/exporter.js b/source/export/exporter.js new file mode 100644 index 0000000..0c1c3b9 --- /dev/null +++ b/source/export/exporter.js @@ -0,0 +1,26 @@ +OV.Exporter = class +{ + constructor () + { + this.exporters = [ + new OV.ExporterObj (), + new OV.ExporterStl (), + new OV.ExporterPly (), + new OV.ExporterOff (), + new OV.ExporterGltf () + ]; + } + + Export (model, format, extension, callbacks) + { + let files = []; + for (let i = 0; i < this.exporters.length; i++) { + let exporter = this.exporters[i]; + if (exporter.CanExport (format, extension)) { + exporter.Export (model, format, files, callbacks); + break; + } + } + return files; + } +}; diff --git a/source/export/exporterbase.js b/source/export/exporterbase.js new file mode 100644 index 0000000..c906e78 --- /dev/null +++ b/source/export/exporterbase.js @@ -0,0 +1,81 @@ +OV.ExportedFile = class +{ + constructor (name) + { + this.name = name; + this.url = null; + this.content = null; + } + + GetName () + { + return this.name; + } + + SetName (name) + { + this.name = name; + } + + GetUrl () + { + return this.url; + } + + SetUrl (url) + { + this.url = url; + } + + GetContent () + { + return this.content; + } + + SetContent (content) + { + this.content = content; + } +}; + +OV.ExporterBase = class +{ + constructor () + { + this.callbacks = null; + } + + CanExport (format, extension) + { + return false; + } + + Export (model, format, files, callbacks) + { + this.callbacks = callbacks; + this.ExportContent (model, format, files); + } + + ExportContent (model, format, files) + { + + } + + GetExportedMaterialName (originalName) + { + return this.GetExportedName (originalName, 'Material'); + } + + GetExportedMeshName (originalName) + { + return this.GetExportedName (originalName, 'Mesh'); + } + + GetExportedName (originalName, defaultName) + { + if (originalName.length === 0) { + return defaultName; + } + return originalName; + } +}; diff --git a/source/export/exportergltf.js b/source/export/exportergltf.js new file mode 100644 index 0000000..39b8498 --- /dev/null +++ b/source/export/exportergltf.js @@ -0,0 +1,404 @@ +OV.ExporterGltf = class extends OV.ExporterBase +{ + constructor () + { + super (); + this.components = { + index : { + type : 5125, // unsigned int 32 + size : 4 + }, + number : { + type : 5126, // float 32 + size : 4 + } + }; + } + + CanExport (format, extension) + { + return (format === OV.FileFormat.Text && extension === 'gltf') || (format === OV.FileFormat.Binary && extension === 'glb'); + } + + ExportContent (model, format, files) + { + if (format === OV.FileFormat.Text) { + this.ExportAsciiContent (model, files); + } else if (format === OV.FileFormat.Binary) { + this.ExportBinaryContent (model, files); + } + } + + ExportAsciiContent (model, files) + { + let gltfFile = new OV.ExportedFile ('model.gltf'); + let binFile = new OV.ExportedFile ('model.bin'); + files.push (gltfFile); + files.push (binFile); + + let meshDataArr = this.GetMeshData (model); + let mainBuffer = this.GetMainBuffer (meshDataArr); + let mainJson = this.GetMainJson (meshDataArr); + mainJson.buffers.push ({ + uri : binFile.GetName (), + byteLength : mainBuffer.byteLength + }); + + let fileNameToIndex = []; + this.ExportMaterials (model, mainJson, function (texture) { + let fileName = OV.GetFileName (texture.name); + let textureIndex = fileNameToIndex[fileName]; + if (textureIndex === undefined) { + let textureFile = new OV.ExportedFile (fileName); + textureFile.SetUrl (texture.url); + files.push (textureFile); + + textureIndex = mainJson.textures.length; + fileNameToIndex[fileName] = textureIndex; + + mainJson.images.push ({ + uri : fileName + }); + mainJson.textures.push ({ + source : textureIndex + }); + } + return textureIndex; + }); + + gltfFile.SetContent (JSON.stringify (mainJson, null, 4)); + binFile.SetContent (mainBuffer); + } + + ExportBinaryContent (model, files) + { + let glbFile = new OV.ExportedFile ('model.glb'); + files.push (glbFile); + + let meshDataArr = this.GetMeshData (model); + let mainBuffer = this.GetMainBuffer (meshDataArr); + let mainJson = this.GetMainJson (meshDataArr); + mainJson.buffers.push ({ + byteLength : mainBuffer.byteLength + }); + + let textureBuffers = []; + let obj = this; + + let fileNameToIndex = []; + this.ExportMaterials (model, mainJson, function (texture) { + let fileName = OV.GetFileName (texture.name); + let extension = OV.GetFileExtension (texture.name); + let textureIndex = fileNameToIndex[fileName]; + if (textureIndex === undefined) { + let bufferIndex = mainJson.buffers.length; + let bufferViewIndex = mainJson.bufferViews.length; + textureIndex = mainJson.textures.length; + fileNameToIndex[fileName] = textureIndex; + let textureBuffer = obj.callbacks.getTextureBuffer (texture.name); + textureBuffers.push (textureBuffer); + mainJson.buffers.push ({ + byteLength : textureBuffer.byteLength + }); + mainJson.bufferViews.push ({ + buffer : bufferIndex, + byteOffset : 0, + byteLength : textureBuffer.byteLength + }); + mainJson.images.push ({ + bufferView : bufferViewIndex, + mimeType : 'image/' + extension + }); + mainJson.textures.push ({ + source : textureIndex + }); + } + return textureIndex; + }); + + let mainJsonString = JSON.stringify (mainJson, null, 4); + let glbSize = 12 + 8 + mainJsonString.length; + for (let i = 0; i < mainJson.buffers.length; i++) { + glbSize += 8 + mainJson.buffers[i].byteLength; + } + + let glbWriter = new OV.BinaryWriter (glbSize, true); + + glbWriter.WriteUnsignedInteger32 (0x46546C67); + glbWriter.WriteUnsignedInteger32 (2); + glbWriter.WriteUnsignedInteger32 (glbSize); + + glbWriter.WriteUnsignedInteger32 (mainJsonString.length); + glbWriter.WriteUnsignedInteger32 (0x4E4F534A); + for (let i = 0; i < mainJsonString.length; i++) { + glbWriter.WriteUnsignedCharacter8 (mainJsonString.charCodeAt (i)); + } + + glbWriter.WriteUnsignedInteger32 (mainBuffer.byteLength); + glbWriter.WriteUnsignedInteger32 (0x004E4942); + glbWriter.WriteArrayBuffer (mainBuffer); + + for (let i = 0; i < textureBuffers.length; i++) { + let textureBuffer = textureBuffers[i]; + glbWriter.WriteUnsignedInteger32 (textureBuffer.byteLength); + glbWriter.WriteUnsignedInteger32 (0x004E4942); + glbWriter.WriteArrayBuffer (textureBuffer); + } + + glbFile.SetContent (glbWriter.GetBuffer ()); + } + + GetMeshData (model) + { + let meshDataArr = []; + + for (let meshIndex = 0; meshIndex < model.MeshCount (); meshIndex++) { + let mesh = model.GetMesh (meshIndex); + let buffer = OV.ConvertMeshToMeshBuffer (mesh); + meshDataArr.push ({ + name : mesh.GetName (), + buffer : buffer, + offsets : [], + sizes : [] + }); + } + + return meshDataArr; + } + + GetMainBuffer (meshDataArr) + { + let mainBufferSize = 0; + for (let meshIndex = 0; meshIndex < meshDataArr.length; meshIndex++) { + let meshData = meshDataArr[meshIndex]; + mainBufferSize += meshData.buffer.GetByteLength (this.components.index.size, this.components.number.size); + } + + let writer = new OV.BinaryWriter (mainBufferSize, true); + for (let meshIndex = 0; meshIndex < meshDataArr.length; meshIndex++) { + let meshData = meshDataArr[meshIndex]; + let primitives = meshData.buffer.primitives; + for (let primitiveIndex = 0; primitiveIndex < primitives.length; primitiveIndex++) { + let primitive = primitives[primitiveIndex]; + let offset = writer.GetPosition (); + for (let i = 0; i < primitive.indices.length; i++) { + writer.WriteUnsignedInteger32 (primitive.indices[i]); + } + for (let i = 0; i < primitive.vertices.length; i++) { + writer.WriteFloat32 (primitive.vertices[i]); + } + for (let i = 0; i < primitive.normals.length; i++) { + writer.WriteFloat32 (primitive.normals[i]); + } + for (let i = 0; i < primitive.uvs.length; i++) { + let texCoord = primitive.uvs[i]; + if (i % 2 === 1) { + texCoord *= -1.0; + } + writer.WriteFloat32 (texCoord); + } + meshData.offsets.push (offset); + meshData.sizes.push (writer.GetPosition () - offset); + } + } + + return writer.GetBuffer (); + } + + GetMainJson (meshDataArr) + { + let mainJson = { + asset : { + version : '2.0' + }, + scene : 0, + scenes : [ + { + nodes : [] + } + ], + nodes : [], + materials : [], + meshes : [], + buffers : [], + bufferViews : [], + accessors : [] + }; + + for (let meshIndex = 0; meshIndex < meshDataArr.length; meshIndex++) { + let meshData = meshDataArr[meshIndex]; + mainJson.scenes[0].nodes.push (meshIndex); + mainJson.nodes.push ({ + mesh : meshIndex + }); + let jsonMesh = { + name : this.GetExportedMeshName (meshData.name), + primitives : [] + }; + + let primitives = meshData.buffer.primitives; + for (let primitiveIndex = 0; primitiveIndex < primitives.length; primitiveIndex++) { + let primitive = primitives[primitiveIndex]; + let bufferViewIndex = mainJson.bufferViews.length; + let accessorIndex = mainJson.accessors.length; + let accessorOffset = 0; + + let jsonPrimitive = { + attributes : { + POSITION : accessorIndex + 1, + NORMAL : accessorIndex + 2 + }, + indices : accessorIndex, + mode : 4, + material : primitive.material + }; + + mainJson.bufferViews.push ({ + buffer : 0, + byteOffset : meshData.offsets[primitiveIndex], + byteLength : meshData.sizes[primitiveIndex] + }); + + mainJson.accessors.push ({ + bufferView : bufferViewIndex, + byteOffset : accessorOffset, + componentType : this.components.index.type, + count : primitive.indices.length, + type : 'SCALAR' + }); + accessorOffset += primitive.indices.length * this.components.index.size; + + mainJson.accessors.push ({ + bufferView : bufferViewIndex, + byteOffset : accessorOffset, + componentType : this.components.number.type, + count : primitive.vertices.length / 3, + type : 'VEC3' + }); + accessorOffset += primitive.vertices.length * this.components.number.size; + + mainJson.accessors.push ({ + bufferView : bufferViewIndex, + byteOffset : accessorOffset, + componentType : this.components.number.type, + count : primitive.normals.length / 3, + type : 'VEC3' + }); + accessorOffset += primitive.normals.length * this.components.number.size; + + if (primitive.uvs.length > 0) { + mainJson.accessors.push ({ + bufferView : bufferViewIndex, + byteOffset : accessorOffset, + componentType : this.components.number.type, + count : primitive.uvs.length / 2, + type : 'VEC2' + }); + accessorOffset += primitive.uvs.length * this.components.number.size; + jsonPrimitive.attributes.TEXCOORD_0 = accessorIndex + 3; + } + + jsonMesh.primitives.push (jsonPrimitive); + } + mainJson.meshes.push (jsonMesh); + } + + return mainJson; + } + + ExportMaterials (model, mainJson, addTexture) + { + function ExportMaterial (obj, mainJson, material, addTexture) + { + function ColorToRGBA (color, opacity) + { + return [ + OV.SRGBToLinear (color.r / 255.0), + OV.SRGBToLinear (color.g / 255.0), + OV.SRGBToLinear (color.b / 255.0), + opacity + ]; + } + + function ColorToRGB (color) + { + return [ + OV.SRGBToLinear (color.r / 255.0), + OV.SRGBToLinear (color.g / 255.0), + OV.SRGBToLinear (color.b / 255.0) + ]; + } + + function GetTextureParams (mainJson, texture, addTexture) + { + if (texture === null || texture.name === null || texture.url === null) { + return null; + } + + if (mainJson.images === undefined) { + mainJson.images = []; + } + if (mainJson.textures === undefined) { + mainJson.textures = []; + } + + let textureIndex = addTexture (texture); + let textureParams = { + index : textureIndex + }; + + if (texture.HasTransformation ()) { + textureParams.extensions = { + KHR_texture_transform : { + offset : [texture.offset.x, -texture.offset.y], + scale : [texture.scale.x, texture.scale.y], + rotation : -texture.rotation + } + }; + } + + return textureParams; + } + + let jsonMaterial = { + name : obj.GetExportedMaterialName (material.name), + pbrMetallicRoughness : { + baseColorFactor : ColorToRGBA (material.diffuse, material.opacity), + metallicFactor : 0.0, + roughnessFactor : 1.0 + }, + emissiveFactor : ColorToRGB (material.emissive), + alphaMode : 'OPAQUE', + alphaCutoff : material.alphaTest + }; + + if (material.transparent) { + // TODO: mask? + jsonMaterial.alphaMode = 'BLEND'; + } + + let baseColorTexture = GetTextureParams (mainJson, material.diffuseMap, addTexture); + if (baseColorTexture !== null) { + if (!material.multiplyDiffuseMap) { + jsonMaterial.pbrMetallicRoughness.baseColorFactor = ColorToRGBA (new OV.Color (255, 255, 255), material.opacity); + } + jsonMaterial.pbrMetallicRoughness.baseColorTexture = baseColorTexture; + } + let normalTexture = GetTextureParams (mainJson, material.normalMap, addTexture); + if (normalTexture !== null) { + jsonMaterial.normalTexture = normalTexture; + } + let emissiveTexture = GetTextureParams (mainJson, material.emissiveMap, addTexture); + if (emissiveTexture !== null) { + jsonMaterial.emissiveTexture = emissiveTexture; + } + + mainJson.materials.push (jsonMaterial); + } + + for (let materialIndex = 0; materialIndex < model.MaterialCount (); materialIndex++) { + let material = model.GetMaterial (materialIndex); + ExportMaterial (this, mainJson, material, addTexture); + } + } +}; diff --git a/source/export/exporterobj.js b/source/export/exporterobj.js new file mode 100644 index 0000000..0267ea7 --- /dev/null +++ b/source/export/exporterobj.js @@ -0,0 +1,107 @@ +OV.ExporterObj = class extends OV.ExporterBase +{ + constructor () + { + super (); + } + + CanExport (format, extension) + { + return format === OV.FileFormat.Text && extension === 'obj'; + } + + ExportContent (model, format, files) + { + function WriteTexture (mtlWriter, keyword, texture, files) + { + if (texture !== null && texture.name !== null && texture.url !== null) { + let fileName = OV.GetFileName (texture.name); + mtlWriter.WriteArrayLine ([keyword, fileName]); + + let fileIndex = files.findIndex (function (file) { + return file.GetName () === fileName; + }); + if (fileIndex === -1) { + let textureFile = new OV.ExportedFile (fileName); + textureFile.SetUrl (texture.url); + files.push (textureFile); + } + } + } + + let mtlFile = new OV.ExportedFile ('model.mtl'); + let objFile = new OV.ExportedFile ('model.obj'); + + files.push (mtlFile); + files.push (objFile); + + let mtlWriter = new OV.TextWriter (); + for (let materialIndex = 0; materialIndex < model.MaterialCount (); materialIndex++) { + let material = model.GetMaterial (materialIndex); + mtlWriter.WriteArrayLine (['newmtl', this.GetExportedMaterialName (material.name)]); + mtlWriter.WriteArrayLine (['Ka', material.ambient.r / 255.0, material.ambient.g / 255.0, material.ambient.b / 255.0]); + mtlWriter.WriteArrayLine (['Kd', material.diffuse.r / 255.0, material.diffuse.g / 255.0, material.diffuse.b / 255.0]); + mtlWriter.WriteArrayLine (['Ks', material.specular.r / 255.0, material.specular.g / 255.0, material.specular.b / 255.0]); + mtlWriter.WriteArrayLine (['Ns', material.shininess * 100.0]); + mtlWriter.WriteArrayLine (['d', material.opacity]); + WriteTexture (mtlWriter, 'map_Kd', material.diffuseMap, files); + WriteTexture (mtlWriter, 'map_Ks', material.specularMap, files); + WriteTexture (mtlWriter, 'bump', material.bumpMap, files); + } + mtlFile.SetContent (mtlWriter.GetText ()); + + let objWriter = new OV.TextWriter (); + objWriter.WriteArrayLine (['mtllib', mtlFile.GetName ()]); + let vertexOffset = 0; + let normalOffset = 0; + let uvOffset = 0; + let usedMaterialName = null; + for (let meshIndex = 0; meshIndex < model.MeshCount (); meshIndex++) { + let mesh = model.GetMesh (meshIndex); + objWriter.WriteArrayLine (['g', this.GetExportedMeshName (mesh.GetName ())]); + for (let vertexIndex = 0; vertexIndex < mesh.VertexCount (); vertexIndex++) { + let vertex = mesh.GetVertex (vertexIndex); + objWriter.WriteArrayLine (['v', vertex.x, vertex.y, vertex.z]); + } + for (let normalIndex = 0; normalIndex < mesh.NormalCount (); normalIndex++) { + let normal = mesh.GetNormal (normalIndex); + objWriter.WriteArrayLine (['vn', normal.x, normal.y, normal.z]); + } + for (let textureUVIndex = 0; textureUVIndex < mesh.TextureUVCount (); textureUVIndex++) { + let uv = mesh.GetTextureUV (textureUVIndex); + objWriter.WriteArrayLine (['vt', uv.x, uv.y]); + } + for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) { + let triangle = mesh.GetTriangle (triangleIndex); + let v0 = triangle.v0 + vertexOffset + 1; + let v1 = triangle.v1 + vertexOffset + 1; + let v2 = triangle.v2 + vertexOffset + 1; + let n0 = triangle.n0 + normalOffset + 1; + let n1 = triangle.n1 + normalOffset + 1; + let n2 = triangle.n2 + normalOffset + 1; + if (triangle.mat !== null) { + let material = model.GetMaterial (triangle.mat); + let materialName = this.GetExportedMaterialName (material.name); + if (materialName !== usedMaterialName) { + objWriter.WriteArrayLine (['usemtl', materialName]); + usedMaterialName = materialName; + } + } + let u0 = ''; + let u1 = ''; + let u2 = ''; + if (triangle.HasTextureUVs ()) { + u0 = triangle.u0 + uvOffset + 1; + u1 = triangle.u1 + uvOffset + 1; + u2 = triangle.u2 + uvOffset + 1; + } + objWriter.WriteArrayLine (['f', [v0, u0, n0].join ('/'), [v1, u1, n1].join ('/'), [v2, u2, n2].join ('/')]); + } + vertexOffset += mesh.VertexCount (); + normalOffset += mesh.NormalCount (); + uvOffset += mesh.TextureUVCount (); + } + + objFile.SetContent (objWriter.GetText ()); + } +}; diff --git a/source/export/exporteroff.js b/source/export/exporteroff.js new file mode 100644 index 0000000..f6187d6 --- /dev/null +++ b/source/export/exporteroff.js @@ -0,0 +1,33 @@ +OV.ExporterOff = class extends OV.ExporterBase +{ + constructor () + { + super (); + } + + CanExport (format, extension) + { + return format === OV.FileFormat.Text && extension === 'off'; + } + + ExportContent (model, format, files) + { + let offFile = new OV.ExportedFile ('model.off'); + files.push (offFile); + + let offWriter = new OV.TextWriter (); + offWriter.WriteLine ('OFF'); + offWriter.WriteArrayLine ([model.VertexCount (), model.TriangleCount (), 0]); + + OV.EnumerateModelVerticesAndTriangles (model, { + onVertex : function (x, y, z) { + offWriter.WriteArrayLine ([x, y, z]); + }, + onTriangle : function (v0, v1, v2) { + offWriter.WriteArrayLine ([3, v0, v1, v2]); + } + }); + + offFile.SetContent (offWriter.GetText ()); + } +}; diff --git a/source/export/exporterply.js b/source/export/exporterply.js new file mode 100644 index 0000000..c150fab --- /dev/null +++ b/source/export/exporterply.js @@ -0,0 +1,94 @@ +OV.ExporterPly = class extends OV.ExporterBase +{ + constructor () + { + super (); + } + + CanExport (format, extension) + { + return (format === OV.FileFormat.Text || format === OV.FileFormat.Binary) && extension === 'ply'; + } + + ExportContent (model, format, files) + { + if (format === OV.FileFormat.Text) { + this.ExportText (model, files); + } else { + this.ExportBinary (model, files); + } + } + + ExportText (model, files) + { + let plyFile = new OV.ExportedFile ('model.ply'); + files.push (plyFile); + + let plyWriter = new OV.TextWriter (); + + let vertexCount = model.VertexCount (); + let triangleCount = model.TriangleCount (); + let headerText = this.GetHeaderText ('ascii', vertexCount, triangleCount); + plyWriter.Write (headerText); + + OV.EnumerateModelVerticesAndTriangles (model, { + onVertex : function (x, y, z) { + plyWriter.WriteArrayLine ([x, y, z]); + }, + onTriangle : function (v0, v1, v2) { + plyWriter.WriteArrayLine ([3, v0, v1, v2]); + } + }); + + plyFile.SetContent (plyWriter.GetText ()); + } + + ExportBinary (model, files) + { + let plyFile = new OV.ExportedFile ('model.ply'); + files.push (plyFile); + + + let vertexCount = model.VertexCount (); + let triangleCount = model.TriangleCount (); + let headerText = this.GetHeaderText ('binary_little_endian', vertexCount, triangleCount); + + let fullByteLength = headerText.length + vertexCount * 3 * 4 + triangleCount * (1 + 3 * 4); + let plyWriter = new OV.BinaryWriter (fullByteLength, true); + + for (let i = 0; i < headerText.length; i++) { + plyWriter.WriteUnsignedCharacter8 (headerText.charCodeAt (i)); + } + + OV.EnumerateModelVerticesAndTriangles (model, { + onVertex : function (x, y, z) { + plyWriter.WriteFloat32 (x); + plyWriter.WriteFloat32 (y); + plyWriter.WriteFloat32 (z); + }, + onTriangle : function (v0, v1, v2) { + plyWriter.WriteUnsignedCharacter8 (3); + plyWriter.WriteInteger32 (v0); + plyWriter.WriteInteger32 (v1); + plyWriter.WriteInteger32 (v2); + } + }); + + plyFile.SetContent (plyWriter.GetBuffer ()); + } + + GetHeaderText (format, vertexCount, triangleCount) + { + let headerWriter = new OV.TextWriter (); + headerWriter.WriteLine ('ply'); + headerWriter.WriteLine ('format ' + format + ' 1.0'); + headerWriter.WriteLine ('element vertex ' + vertexCount); + headerWriter.WriteLine ('property float x'); + headerWriter.WriteLine ('property float y'); + headerWriter.WriteLine ('property float z'); + headerWriter.WriteLine ('element face ' + triangleCount); + headerWriter.WriteLine ('property list uchar int vertex_index'); + headerWriter.WriteLine ('end_header'); + return headerWriter.GetText (); + } +}; diff --git a/source/export/exporterstl.js b/source/export/exporterstl.js new file mode 100644 index 0000000..8d4665e --- /dev/null +++ b/source/export/exporterstl.js @@ -0,0 +1,84 @@ +OV.ExporterStl = class extends OV.ExporterBase +{ + constructor () + { + super (); + } + + CanExport (format, extension) + { + return (format === OV.FileFormat.Text || format === OV.FileFormat.Binary) && extension === 'stl'; + } + + ExportContent (model, format, files) + { + if (format === OV.FileFormat.Text) { + this.ExportText (model, files); + } else { + this.ExportBinary (model, files); + } + } + + ExportText (model, files) + { + let stlFile = new OV.ExportedFile ('model.stl'); + files.push (stlFile); + + let stlWriter = new OV.TextWriter (); + stlWriter.WriteLine ('solid Model'); + OV.EnumerateModelTrianglesWithNormals (model, function (v0, v1, v2, normal) { + stlWriter.WriteArrayLine (['facet', 'normal', normal.x, normal.y, normal.z]); + stlWriter.Indent (1); + stlWriter.WriteLine ('outer loop'); + stlWriter.Indent (1); + stlWriter.WriteArrayLine (['vertex', v0.x, v0.y, v0.z]); + stlWriter.WriteArrayLine (['vertex', v1.x, v1.y, v1.z]); + stlWriter.WriteArrayLine (['vertex', v2.x, v2.y, v2.z]); + stlWriter.Indent (-1); + stlWriter.WriteLine ('endloop'); + stlWriter.Indent (-1); + stlWriter.WriteLine ('endfacet'); + }); + stlWriter.WriteLine ('endsolid Model'); + + stlFile.SetContent (stlWriter.GetText ()); + } + + ExportBinary (model, files) + { + let stlFile = new OV.ExportedFile ('model.stl'); + files.push (stlFile); + + let triangleCount = model.TriangleCount (); + let headerSize = 80; + let fullByteLength = headerSize + 4 + triangleCount * 50; + let stlWriter = new OV.BinaryWriter (fullByteLength, true); + + for (let i = 0; i < headerSize; i++) { + stlWriter.WriteUnsignedCharacter8 (0); + } + + stlWriter.WriteUnsignedInteger32 (triangleCount); + OV.EnumerateModelTrianglesWithNormals (model, function (v0, v1, v2, normal) { + stlWriter.WriteFloat32 (normal.x); + stlWriter.WriteFloat32 (normal.y); + stlWriter.WriteFloat32 (normal.z); + + stlWriter.WriteFloat32 (v0.x); + stlWriter.WriteFloat32 (v0.y); + stlWriter.WriteFloat32 (v0.z); + + stlWriter.WriteFloat32 (v1.x); + stlWriter.WriteFloat32 (v1.y); + stlWriter.WriteFloat32 (v1.z); + + stlWriter.WriteFloat32 (v2.x); + stlWriter.WriteFloat32 (v2.y); + stlWriter.WriteFloat32 (v2.z); + + stlWriter.WriteUnsignedInteger16 (0); + }); + + stlFile.SetContent (stlWriter.GetBuffer ()); + } +}; diff --git a/source/export/exporterutils.js b/source/export/exporterutils.js new file mode 100644 index 0000000..addca26 --- /dev/null +++ b/source/export/exporterutils.js @@ -0,0 +1,34 @@ +OV.EnumerateModelVerticesAndTriangles = function (model, callbacks) +{ + for (let meshIndex = 0; meshIndex < model.MeshCount (); meshIndex++) { + let mesh = model.GetMesh (meshIndex); + for (let vertexIndex = 0; vertexIndex < mesh.VertexCount (); vertexIndex++) { + let vertex = mesh.GetVertex (vertexIndex); + callbacks.onVertex (vertex.x, vertex.y, vertex.z); + } + } + let vertexOffset = 0; + for (let meshIndex = 0; meshIndex < model.MeshCount (); meshIndex++) { + let mesh = model.GetMesh (meshIndex); + for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) { + let triangle = mesh.GetTriangle (triangleIndex); + callbacks.onTriangle (triangle.v0 + vertexOffset, triangle.v1 + vertexOffset, triangle.v2 + vertexOffset); + } + vertexOffset += mesh.VertexCount (); + } +}; + +OV.EnumerateModelTrianglesWithNormals = function (model, onTriangle) +{ + for (let meshIndex = 0; meshIndex < model.MeshCount (); meshIndex++) { + let mesh = model.GetMesh (meshIndex); + for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) { + let triangle = mesh.GetTriangle (triangleIndex); + let v0 = mesh.GetVertex (triangle.v0); + let v1 = mesh.GetVertex (triangle.v1); + let v2 = mesh.GetVertex (triangle.v2); + let normal = OV.CalculateTriangleNormal (v0, v1, v2); + onTriangle (v0, v1, v2, normal); + } + } +}; diff --git a/source/external/three.converter.js b/source/external/three.converter.js new file mode 100644 index 0000000..f8b16d7 --- /dev/null +++ b/source/external/three.converter.js @@ -0,0 +1,195 @@ +OV.ConvertModelToThreeMeshes = function (model, callbacks) +{ + function CreateThreeMaterial (model, materialIndex) + { + function SetTextureParameters (texture, threeTexture) + { + threeTexture.wrapS = THREE.RepeatWrapping; + threeTexture.wrapT = THREE.RepeatWrapping; + threeTexture.rotation = texture.rotation; + threeTexture.offset.x = texture.offset.x; + threeTexture.offset.y = texture.offset.y; + threeTexture.repeat.x = texture.scale.x; + threeTexture.repeat.y = texture.scale.y; + } + + function LoadTexture (threeMaterial, texture, onLoad) + { + if (texture === null || texture.url === null) { + return; + } + let loader = new THREE.TextureLoader (); + loader.load (texture.url, function (threeTexture) { + SetTextureParameters (texture, threeTexture); + threeTexture.image = OV.ResizeImageToPowerOfTwoSides (threeTexture.image); + threeMaterial.needsUpdate = true; + onLoad (threeTexture); + }); + } + + let material = model.GetMaterial (materialIndex); + let diffuseColor = new THREE.Color (material.diffuse.r / 255.0, material.diffuse.g / 255.0, material.diffuse.b / 255.0); + let specularColor = new THREE.Color (material.specular.r / 255.0, material.specular.g / 255.0, material.specular.b / 255.0); + let emissiveColor = new THREE.Color (material.emissive.r / 255.0, material.emissive.g / 255.0, material.emissive.b / 255.0); + if (OV.IsEqual (material.shininess, 0.0)) { + specularColor.setRGB (0.0, 0.0, 0.0); + } + + let threeMaterial = new THREE.MeshPhongMaterial ({ + color : diffuseColor, + specular : specularColor, + emissive : emissiveColor, + shininess : material.shininess * 10.0, + opacity : material.opacity, + transparent : material.transparent, + alphaTest : material.alphaTest, + side : THREE.DoubleSide + }); + + LoadTexture (threeMaterial, material.diffuseMap, function (threeTexture) { + if (!material.multiplyDiffuseMap) { + threeMaterial.color.setRGB (1.0, 1.0, 1.0); + } + threeMaterial.map = threeTexture; + callbacks.onTextureLoaded (); + }); + LoadTexture (threeMaterial, material.specularMap, function (threeTexture) { + threeMaterial.specularMap = threeTexture; + callbacks.onTextureLoaded (); + }); + LoadTexture (threeMaterial, material.bumpMap, function (threeTexture) { + threeMaterial.bumpMap = threeTexture; + callbacks.onTextureLoaded (); + }); + LoadTexture (threeMaterial, material.normalMap, function (threeTexture) { + threeMaterial.normalMap = threeTexture; + callbacks.onTextureLoaded (); + }); + LoadTexture (threeMaterial, material.emissiveMap, function (threeTexture) { + threeMaterial.emissiveMap = threeTexture; + callbacks.onTextureLoaded (); + }); + + return threeMaterial; + } + + function CreateThreeMesh (model, meshIndex, modelThreeMaterials) + { + let mesh = model.GetMesh (meshIndex); + let triangleCount = mesh.TriangleCount (); + if (triangleCount === 0) { + return null; + } + + let triangleIndices = []; + for (let i = 0; i < triangleCount; i++) { + triangleIndices.push (i); + } + triangleIndices.sort (function (a, b) { + let aTriangle = mesh.GetTriangle (a); + let bTriangle = mesh.GetTriangle (b); + return aTriangle.mat - bTriangle.mat; + }); + + let threeGeometry = new THREE.BufferGeometry (); + let meshThreeMaterials = []; + let meshOriginalMaterials = []; + let modelToThreeMaterials = {}; + + let vertices = []; + let normals = []; + let uvs = []; + + let groups = []; + groups.push ({ + start : 0, + end : -1 + }); + + let meshHasUVs = mesh.TextureUVCount () > 0; + for (let i = 0; i < triangleIndices.length; i++) { + let triangleIndex = triangleIndices[i]; + let triangle = mesh.GetTriangle (triangleIndex); + + let v0 = mesh.GetVertex (triangle.v0); + let v1 = mesh.GetVertex (triangle.v1); + let v2 = mesh.GetVertex (triangle.v2); + vertices.push (v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, v2.x, v2.y, v2.z); + + let n0 = mesh.GetNormal (triangle.n0); + let n1 = mesh.GetNormal (triangle.n1); + let n2 = mesh.GetNormal (triangle.n2); + normals.push (n0.x, n0.y, n0.z, n1.x, n1.y, n1.z, n2.x, n2.y, n2.z); + + if (triangle.HasTextureUVs ()) { + let u0 = mesh.GetTextureUV (triangle.u0); + let u1 = mesh.GetTextureUV (triangle.u1); + let u2 = mesh.GetTextureUV (triangle.u2); + uvs.push (u0.x, u0.y, u1.x, u1.y, u2.x, u2.y); + } else if (meshHasUVs) { + uvs.push (0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + } + + let modelMaterialIndex = triangle.mat; + let materialIndex = modelToThreeMaterials[modelMaterialIndex]; + if (materialIndex === undefined) { + materialIndex = meshThreeMaterials.length; + modelToThreeMaterials[modelMaterialIndex] = materialIndex; + meshThreeMaterials.push (modelThreeMaterials[modelMaterialIndex]); + meshOriginalMaterials.push (modelMaterialIndex); + if (i > 0) { + groups[groups.length - 1].end = i - 1; + groups.push ({ + start : groups[groups.length - 1].end + 1, + end : -1 + }); + } + } + } + + groups[groups.length - 1].end = triangleCount - 1; + + threeGeometry.setAttribute ('position', new THREE.Float32BufferAttribute (vertices, 3)); + threeGeometry.setAttribute ('normal', new THREE.Float32BufferAttribute (normals, 3)); + if (uvs.length !== 0) { + threeGeometry.setAttribute ('uv', new THREE.Float32BufferAttribute (uvs, 2)); + } + for (let i = 0; i < groups.length; i++) { + let group = groups[i]; + threeGeometry.addGroup (group.start * 3, (group.end - group.start + 1) * 3, i); + } + + let threeMesh = new THREE.Mesh (threeGeometry, meshThreeMaterials); + threeMesh.userData = { + originalMeshIndex : meshIndex, + originalMaterials : meshOriginalMaterials, + threeMaterials : null + }; + + return threeMesh; + } + + let modelThreeMaterials = []; + for (let materialIndex = 0; materialIndex < model.MaterialCount (); materialIndex++) { + let threeMaterial = CreateThreeMaterial (model, materialIndex); + modelThreeMaterials.push (threeMaterial); + } + + let threeMeshes = []; + let taskRunner = new OV.TaskRunner (); + taskRunner.Run (model.MeshCount (), { + runTask : function (index, ready) { + let mesh = model.GetMesh (index); + if (mesh.TriangleCount () === 0) { + ready (); + } else { + let threeMesh = CreateThreeMesh (model, index, modelThreeMaterials); + threeMeshes.push (threeMesh); + ready (); + } + }, + onReady : function () { + callbacks.onModelLoaded (threeMeshes); + } + }); +}; diff --git a/source/external/three.model.loader.js b/source/external/three.model.loader.js new file mode 100644 index 0000000..c8ca354 --- /dev/null +++ b/source/external/three.model.loader.js @@ -0,0 +1,85 @@ +OV.ThreeModelLoader = class +{ + constructor () + { + this.importer = new OV.Importer (); + this.callbacks = null; + this.inProgress = false; + } + + Init (callbacks) + { + this.callbacks = callbacks; + } + + LoadFromUrlList (urls) + { + if (this.inProgress) { + return; + } + + let obj = this; + this.inProgress = true; + this.callbacks.onLoadStart (); + this.importer.LoadFilesFromUrls (urls, function () { + obj.OnFilesLoaded (); + }); + } + + LoadFromFileList (files) + { + if (this.inProgress) { + return; + } + + let obj = this; + this.inProgress = true; + this.callbacks.onLoadStart (); + this.importer.LoadFilesFromFileObjects (files, function () { + obj.OnFilesLoaded (); + }); + } + + OnFilesLoaded () + { + let obj = this; + this.callbacks.onFilesLoaded (); + + let taskRunner = new OV.TaskRunner (); + taskRunner.Run (1, { + runTask : function (index, ready) { + obj.importer.Import ({ + success : function (importResult) { + obj.OnModelImported (importResult); + ready (); + }, + error : function (importerError) { + obj.callbacks.onLoadError (importerError); + obj.inProgress = false; + ready (); + } + }); + } + }); + } + + OnModelImported (importResult) + { + let obj = this; + this.callbacks.onModelImported (); + OV.ConvertModelToThreeMeshes (importResult.model, { + onTextureLoaded : function () { + obj.callbacks.onTextureLoaded (); + }, + onModelLoaded : function (meshes) { + obj.callbacks.onModelFinished (importResult, meshes); + obj.inProgress = false; + } + }); + } + + GetImporter () + { + return this.importer; + } +}; diff --git a/source/geometry/coord2d.js b/source/geometry/coord2d.js new file mode 100644 index 0000000..493393b --- /dev/null +++ b/source/geometry/coord2d.js @@ -0,0 +1,33 @@ +OV.Coord2D = class +{ + constructor (x, y) + { + this.x = x; + this.y = y; + } + + Clone () + { + return new OV.Coord2D (this.x, this.y); + } +}; + +OV.CoordIsEqual2D = function (a, b) +{ + return OV.IsEqual (a.x, b.x) && OV.IsEqual (a.y, b.y); +}; + +OV.AddCoord2D = function (a, b) +{ + return new OV.Coord2D (a.x + b.x, a.y + b.y); +}; + +OV.SubCoord2D = function (a, b) +{ + return new OV.Coord2D (a.x - b.x, a.y - b.y); +}; + +OV.CoordDistance2D = function (a, b) +{ + return Math.sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); +}; diff --git a/source/geometry/coord3d.js b/source/geometry/coord3d.js new file mode 100644 index 0000000..e3f3d35 --- /dev/null +++ b/source/geometry/coord3d.js @@ -0,0 +1,119 @@ +OV.Coord3D = class +{ + constructor (x, y, z) + { + this.x = x; + this.y = y; + this.z = z; + } + + Length () + { + return Math.sqrt (this.x * this.x + this.y * this.y + this.z * this.z); + } + + MultiplyScalar (scalar) + { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + return this; + } + + Normalize () + { + let length = this.Length (); + if (length > 0.0) { + this.MultiplyScalar (1.0 / length); + } + return this; + } + + Offset (direction, distance) + { + let normal = direction.Clone ().Normalize (); + this.x += normal.x * distance; + this.y += normal.y * distance; + this.z += normal.z * distance; + return this; + } + + Rotate (axis, angle, origo) + { + let normal = axis.Clone ().Normalize (); + + let u = normal.x; + let v = normal.y; + let w = normal.z; + + let x = this.x - origo.x; + let y = this.y - origo.y; + let z = this.z - origo.z; + + let si = Math.sin (angle); + let co = Math.cos (angle); + this.x = - u * (- u * x - v * y - w * z) * (1.0 - co) + x * co + (- w * y + v * z) * si; + this.y = - v * (- u * x - v * y - w * z) * (1.0 - co) + y * co + (w * x - u * z) * si; + this.z = - w * (- u * x - v * y - w * z) * (1.0 - co) + z * co + (- v * x + u * y) * si; + + this.x += origo.x; + this.y += origo.y; + this.z += origo.z; + return this; + } + + Clone () + { + return new OV.Coord3D (this.x, this.y, this.z); + } +}; + +OV.CoordIsEqual3D = function (a, b) +{ + return OV.IsEqual (a.x, b.x) && OV.IsEqual (a.y, b.y) && OV.IsEqual (a.z, b.z); +}; + +OV.AddCoord3D = function (a, b) +{ + return new OV.Coord3D (a.x + b.x, a.y + b.y, a.z + b.z); +}; + +OV.SubCoord3D = function (a, b) +{ + return new OV.Coord3D (a.x - b.x, a.y - b.y, a.z - b.z); +}; + +OV.CoordDistance3D = function (a, b) +{ + return Math.sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z)); +}; + +OV.VectorAngle3D = function (a, b) +{ + let aDirection = a.Clone ().Normalize (); + let bDirection = b.Clone ().Normalize (); + if (OV.CoordIsEqual3D (aDirection, bDirection)) { + return 0.0; + } + let product = OV.DotVector3D (aDirection, bDirection); + return Math.acos (product); +}; + +OV.DotVector3D = function (a, b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +}; + +OV.CrossVector3D = function (a, b) +{ + let result = new OV.Coord3D (0.0, 0.0, 0.0); + result.x = a.y * b.z - a.z * b.y; + result.y = a.z * b.x - a.x * b.z; + result.z = a.x * b.y - a.y * b.x; + return result; +}; + +OV.VectorLength3D = function (x, y, z) +{ + return Math.sqrt (x * x + y * y + z * z); +}; diff --git a/source/geometry/geometry.js b/source/geometry/geometry.js new file mode 100644 index 0000000..b4893be --- /dev/null +++ b/source/geometry/geometry.js @@ -0,0 +1,40 @@ +OV.Eps = 0.00000001; +OV.RadDeg = 57.29577951308232; +OV.DegRad = 0.017453292519943; + +OV.IsZero = function (a) +{ + return Math.abs (a) < OV.Eps; +}; + +OV.IsLower = function (a, b) +{ + return b - a > OV.Eps; +}; + +OV.IsGreater = function (a, b) +{ + return a - b > OV.Eps; +}; + +OV.IsEqual = function (a, b) +{ + return Math.abs (b - a) < OV.Eps; +}; + +OV.IsPositive = function (a) +{ + return a > OV.Eps; +}; + +OV.IsNegative = function (a) +{ + return a < -OV.Eps; +}; + +OV.Direction = +{ + X : 1, + Y : 2, + Z : 3 +}; diff --git a/source/geometry/matrix.js b/source/geometry/matrix.js new file mode 100644 index 0000000..46022a2 --- /dev/null +++ b/source/geometry/matrix.js @@ -0,0 +1,396 @@ +OV.Matrix = class +{ + constructor (matrix) + { + this.matrix = null; + if (matrix !== undefined && matrix !== null) { + this.matrix = matrix; + } + } + + IsValid () + { + return this.matrix !== null; + } + + Set (matrix) + { + this.matrix = matrix; + return this; + } + + Get () + { + return this.matrix; + } + + Clone () + { + let result = [ + this.matrix[0], this.matrix[1], this.matrix[2], this.matrix[3], + this.matrix[4], this.matrix[5], this.matrix[6], this.matrix[7], + this.matrix[8], this.matrix[9], this.matrix[10], this.matrix[11], + this.matrix[12], this.matrix[13], this.matrix[14], this.matrix[15] + ]; + return new OV.Matrix (result); + } + + CreateIdentity () + { + this.matrix = [ + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ]; + return this; + } + + IsIdentity () + { + let identity = new OV.Matrix ().CreateIdentity ().Get (); + for (let i = 0; i < 16; i++) { + if (!OV.IsEqual (this.matrix[i], identity[i])) { + return false; + } + } + return true; + } + + CreateTranslation (x, y, z) + { + this.matrix = [ + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + x, y, z, 1.0 + ]; + return this; + } + + CreateRotation (x, y, z, w) + { + let x2 = x + x; + let y2 = y + y; + let z2 = z + z; + let xx = x * x2; + let xy = x * y2; + let xz = x * z2; + let yy = y * y2; + let yz = y * z2; + let zz = z * z2; + let wx = w * x2; + let wy = w * y2; + let wz = w * z2; + this.matrix = [ + 1.0 - (yy + zz), xy + wz, xz - wy, 0.0, + xy - wz, 1.0 - (xx + zz), yz + wx, 0.0, + xz + wy, yz - wx, 1.0 - (xx + yy), 0.0, + 0.0, 0.0, 0.0, 1.0 + ]; + return this; + } + + CreateScale (x, y, z) + { + this.matrix = [ + x, 0.0, 0.0, 0.0, + 0.0, y, 0.0, 0.0, + 0.0, 0.0, z, 0.0, + 0.0, 0.0, 0.0, 1.0 + ]; + return this; + } + + ComposeTRS (translation, rotation, scale) + { + let tx = translation[0]; + let ty = translation[1]; + let tz = translation[2]; + let qx = rotation[0]; + let qy = rotation[1]; + let qz = rotation[2]; + let qw = rotation[3]; + let sx = scale[0]; + let sy = scale[1]; + let sz = scale[2]; + + let x2 = qx + qx; + let y2 = qy + qy; + let z2 = qz + qz; + let xx = qx * x2; + let xy = qx * y2; + let xz = qx * z2; + let yy = qy * y2; + let yz = qy * z2; + let zz = qz * z2; + let wx = qw * x2; + let wy = qw * y2; + let wz = qw * z2; + + this.matrix = [ + (1.0 - (yy + zz)) * sx, (xy + wz) * sx, (xz - wy) * sx, 0.0, + (xy - wz) * sy, (1.0 - (xx + zz)) * sy, (yz + wx) * sy, 0.0, + (xz + wy) * sz, (yz - wx) * sz, (1.0 - (xx + yy)) * sz, 0.0, + tx, ty, tz, 1.0 + ]; + return this; + } + + DecomposeTRS () + { + let translation = [ + this.matrix[12], + this.matrix[13], + this.matrix[14] + ]; + + let sx = OV.VectorLength3D (this.matrix[0], this.matrix[1], this.matrix[2]); + let sy = OV.VectorLength3D (this.matrix[4], this.matrix[5], this.matrix[6]); + let sz = OV.VectorLength3D (this.matrix[8], this.matrix[9], this.matrix[10]); + let determinant = this.Determinant (); + if (OV.IsNegative (determinant)) { + sx *= -1.0; + } + let scale = [sx, sy, sz]; + + let m00 = this.matrix[0] / sx; + let m01 = this.matrix[4] / sy; + let m02 = this.matrix[8] / sz; + let m10 = this.matrix[1] / sx; + let m11 = this.matrix[5] / sy; + let m12 = this.matrix[9] / sz; + let m20 = this.matrix[2] / sx; + let m21 = this.matrix[6] / sy; + let m22 = this.matrix[10] / sz; + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + let rotation = null; + let tr = m00 + m11 + m22; + if (tr > 0.0) { + let s = Math.sqrt (tr + 1.0) * 2.0; + rotation = [ + (m21 - m12) / s, + (m02 - m20) / s, + (m10 - m01) / s, + 0.25 * s + ]; + } else if ((m00 > m11) && (m00 > m22)) { + let s = Math.sqrt (1.0 + m00 - m11 - m22) * 2.0; + rotation = [ + 0.25 * s, + (m01 + m10) / s, + (m02 + m20) / s, + (m21 - m12) / s + ]; + } else if (m11 > m22) { + let s = Math.sqrt (1.0 + m11 - m00 - m22) * 2.0; + rotation = [ + (m01 + m10) / s, + 0.25 * s, + (m12 + m21) / s, + (m02 - m20) / s + ]; + } else { + let s = Math.sqrt (1.0 + m22 - m00 - m11) * 2.0; + rotation = [ + (m02 + m20) / s, + (m12 + m21) / s, + 0.25 * s, + (m10 - m01) / s + ]; + } + + return { + translation : translation, + rotation : rotation, + scale : scale + }; + } + + Determinant () + { + let a00 = this.matrix[0]; + let a01 = this.matrix[1]; + let a02 = this.matrix[2]; + let a03 = this.matrix[3]; + let a10 = this.matrix[4]; + let a11 = this.matrix[5]; + let a12 = this.matrix[6]; + let a13 = this.matrix[7]; + let a20 = this.matrix[8]; + let a21 = this.matrix[9]; + let a22 = this.matrix[10]; + let a23 = this.matrix[11]; + let a30 = this.matrix[12]; + let a31 = this.matrix[13]; + let a32 = this.matrix[14]; + let a33 = this.matrix[15]; + + let b00 = a00 * a11 - a01 * a10; + let b01 = a00 * a12 - a02 * a10; + let b02 = a00 * a13 - a03 * a10; + let b03 = a01 * a12 - a02 * a11; + let b04 = a01 * a13 - a03 * a11; + let b05 = a02 * a13 - a03 * a12; + let b06 = a20 * a31 - a21 * a30; + let b07 = a20 * a32 - a22 * a30; + let b08 = a20 * a33 - a23 * a30; + let b09 = a21 * a32 - a22 * a31; + let b10 = a21 * a33 - a23 * a31; + let b11 = a22 * a33 - a23 * a32; + + let determinant = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + return determinant; + } + + Invert () + { + let a00 = this.matrix[0]; + let a01 = this.matrix[1]; + let a02 = this.matrix[2]; + let a03 = this.matrix[3]; + let a10 = this.matrix[4]; + let a11 = this.matrix[5]; + let a12 = this.matrix[6]; + let a13 = this.matrix[7]; + let a20 = this.matrix[8]; + let a21 = this.matrix[9]; + let a22 = this.matrix[10]; + let a23 = this.matrix[11]; + let a30 = this.matrix[12]; + let a31 = this.matrix[13]; + let a32 = this.matrix[14]; + let a33 = this.matrix[15]; + + let b00 = a00 * a11 - a01 * a10; + let b01 = a00 * a12 - a02 * a10; + let b02 = a00 * a13 - a03 * a10; + let b03 = a01 * a12 - a02 * a11; + let b04 = a01 * a13 - a03 * a11; + let b05 = a02 * a13 - a03 * a12; + let b06 = a20 * a31 - a21 * a30; + let b07 = a20 * a32 - a22 * a30; + let b08 = a20 * a33 - a23 * a30; + let b09 = a21 * a32 - a22 * a31; + let b10 = a21 * a33 - a23 * a31; + let b11 = a22 * a33 - a23 * a32; + + let determinant = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + if (OV.IsEqual (determinant, 0.0)) { + return null; + } + + let result = [ + (a11 * b11 - a12 * b10 + a13 * b09) / determinant, + (a02 * b10 - a01 * b11 - a03 * b09) / determinant, + (a31 * b05 - a32 * b04 + a33 * b03) / determinant, + (a22 * b04 - a21 * b05 - a23 * b03) / determinant, + (a12 * b08 - a10 * b11 - a13 * b07) / determinant, + (a00 * b11 - a02 * b08 + a03 * b07) / determinant, + (a32 * b02 - a30 * b05 - a33 * b01) / determinant, + (a20 * b05 - a22 * b02 + a23 * b01) / determinant, + (a10 * b10 - a11 * b08 + a13 * b06) / determinant, + (a01 * b08 - a00 * b10 - a03 * b06) / determinant, + (a30 * b04 - a31 * b02 + a33 * b00) / determinant, + (a21 * b02 - a20 * b04 - a23 * b00) / determinant, + (a11 * b07 - a10 * b09 - a12 * b06) / determinant, + (a00 * b09 - a01 * b07 + a02 * b06) / determinant, + (a31 * b01 - a30 * b03 - a32 * b00) / determinant, + (a20 * b03 - a21 * b01 + a22 * b00) / determinant + ]; + + return new OV.Matrix (result); + } + + MultiplyVector (vector) + { + let a00 = vector[0]; + let a01 = vector[1]; + let a02 = vector[2]; + let a03 = vector[3]; + + let b00 = this.matrix[0]; + let b01 = this.matrix[1]; + let b02 = this.matrix[2]; + let b03 = this.matrix[3]; + let b10 = this.matrix[4]; + let b11 = this.matrix[5]; + let b12 = this.matrix[6]; + let b13 = this.matrix[7]; + let b20 = this.matrix[8]; + let b21 = this.matrix[9]; + let b22 = this.matrix[10]; + let b23 = this.matrix[11]; + let b30 = this.matrix[12]; + let b31 = this.matrix[13]; + let b32 = this.matrix[14]; + let b33 = this.matrix[15]; + + let result = [ + a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30, + a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31, + a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32, + a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33 + ]; + return result; + } + + MultiplyMatrix (matrix) + { + let a00 = this.matrix[0]; + let a01 = this.matrix[1]; + let a02 = this.matrix[2]; + let a03 = this.matrix[3]; + let a10 = this.matrix[4]; + let a11 = this.matrix[5]; + let a12 = this.matrix[6]; + let a13 = this.matrix[7]; + let a20 = this.matrix[8]; + let a21 = this.matrix[9]; + let a22 = this.matrix[10]; + let a23 = this.matrix[11]; + let a30 = this.matrix[12]; + let a31 = this.matrix[13]; + let a32 = this.matrix[14]; + let a33 = this.matrix[15]; + + let b00 = matrix.matrix[0]; + let b01 = matrix.matrix[1]; + let b02 = matrix.matrix[2]; + let b03 = matrix.matrix[3]; + let b10 = matrix.matrix[4]; + let b11 = matrix.matrix[5]; + let b12 = matrix.matrix[6]; + let b13 = matrix.matrix[7]; + let b20 = matrix.matrix[8]; + let b21 = matrix.matrix[9]; + let b22 = matrix.matrix[10]; + let b23 = matrix.matrix[11]; + let b30 = matrix.matrix[12]; + let b31 = matrix.matrix[13]; + let b32 = matrix.matrix[14]; + let b33 = matrix.matrix[15]; + + let result = [ + a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30, + a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31, + a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32, + a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33, + a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30, + a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31, + a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32, + a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33, + a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30, + a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31, + a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32, + a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33, + a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30, + a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31, + a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32, + a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33 + ]; + + return new OV.Matrix (result); + } +}; diff --git a/source/geometry/transformation.js b/source/geometry/transformation.js new file mode 100644 index 0000000..558aaf4 --- /dev/null +++ b/source/geometry/transformation.js @@ -0,0 +1,41 @@ +OV.Transformation = class +{ + constructor (matrix) + { + if (matrix !== undefined && matrix !== null) { + this.matrix = matrix; + } else { + this.matrix = new OV.Matrix (); + this.matrix.CreateIdentity (); + } + } + + SetMatrix (matrix) + { + this.matrix = matrix; + return this; + } + + GetMatrix () + { + return this.matrix; + } + + IsIdentity () + { + return this.matrix.IsIdentity (); + } + + AppendMatrix (matrix) + { + this.matrix = this.matrix.MultiplyMatrix (matrix); + return this; + } + + TransformCoord3D (coord) + { + let resultVector = this.matrix.MultiplyVector ([coord.x, coord.y, coord.z, 1.0]); + let result = new OV.Coord3D (resultVector[0], resultVector[1], resultVector[2]); + return result; + } +}; diff --git a/source/geometry/tween.js b/source/geometry/tween.js new file mode 100644 index 0000000..f212826 --- /dev/null +++ b/source/geometry/tween.js @@ -0,0 +1,29 @@ +OV.BezierTweenFunction = function (distance, index, count) +{ + let t = index / count; + return distance * (t * t * (3.0 - 2.0 * t)); +}; + +OV.LinearTweenFunction = function (distance, index, count) +{ + return index * distance / count; +}; + +OV.ParabolicTweenFunction = function (distance, index, count) +{ + let t = index / count; + let t2 = t * t; + return distance * (t2 / (2.0 * (t2 - t) + 1.0)); +}; + +OV.TweenCoord3D = function (a, b, count, tweenFunc) +{ + let dir = OV.SubCoord3D (b, a).Normalize (); + let distance = OV.CoordDistance3D (a, b); + let result = []; + for (let i = 0; i < count; i++) { + let step = tweenFunc (distance, i, count - 1); + result.push (a.Clone ().Offset (dir, step)); + } + return result; +}; diff --git a/source/import/importer.js b/source/import/importer.js new file mode 100644 index 0000000..d8ab5a5 --- /dev/null +++ b/source/import/importer.js @@ -0,0 +1,354 @@ +OV.File = class +{ + constructor (file, source) + { + this.source = source; + if (source === OV.FileSource.Url) { + this.fileUrl = file; + this.fileObject = null; + this.name = OV.GetFileName (file); + this.extension = OV.GetFileExtension (file); + } else if (source === OV.FileSource.File) { + this.fileUrl = null; + this.fileObject = file; + this.name = OV.GetFileName (file.name); + this.extension = OV.GetFileExtension (file.name); + } + this.content = null; + } +}; + +OV.FileList = class +{ + constructor (importers) + { + this.files = []; + this.importers = importers; + } + + FillFromFileUrls (fileList) + { + this.Fill (fileList, OV.FileSource.Url); + } + + FillFromFileObjects (fileList) + { + this.Fill (fileList, OV.FileSource.File); + } + + ExtendFromFileList (files) + { + for (let i = 0; i < files.length; i++) { + let file = files[i]; + if (!this.ContainsFileByPath (file.name)) { + this.files.push (file); + } + } + } + + GetFiles () + { + return this.files; + } + + GetContent (onReady) + { + let obj = this; + let taskRunner = new OV.TaskRunner (); + taskRunner.Run (this.files.length, { + runTask : function (index, complete) { + obj.GetFileContent (obj.files[index], complete); + }, + onReady : onReady + }); + } + + ContainsFileByPath (filePath) + { + return this.FindFileByPath (filePath) !== null; + } + + FindFileByPath (filePath) + { + let fileName = OV.GetFileName (filePath).toLowerCase (); + for (let fileIndex = 0; fileIndex < this.files.length; fileIndex++) { + let file = this.files[fileIndex]; + if (file.name.toLowerCase () === fileName) { + return file; + } + } + return null; + } + + HasMainFile () + { + return this.GetMainFile () !== null; + } + + GetMainFile () + { + for (let fileIndex = 0; fileIndex < this.files.length; fileIndex++) { + let file = this.files[fileIndex]; + let importer = this.FindImporter (file); + if (importer !== null) { + return { + file : file, + importer : importer + }; + } + } + return null; + } + + IsOnlySource (source) + { + if (this.files.length === 0) { + return false; + } + for (let i = 0; i < this.files.length; i++) { + let file = this.files[i]; + if (file.source !== source) { + return false; + } + } + return true; + } + + Fill (fileList, fileSource) + { + this.files = []; + for (let fileIndex = 0; fileIndex < fileList.length; fileIndex++) { + let fileObject = fileList[fileIndex]; + let file = new OV.File (fileObject, fileSource); + this.AddFile (file); + } + } + + AddFile (file) + { + this.files.push (file); + } + + GetFileContent (file, complete) + { + let callbacks = { + success : function (content) { + file.content = content; + }, + error : function () { + + }, + complete : function () { + complete (); + } + }; + if (file.content !== null) { + complete (); + return; + } + let format = this.GetFileFormat (file); + if (file.source === OV.FileSource.Url) { + OV.RequestUrl (file.fileUrl, format, callbacks); + } else if (file.source === OV.FileSource.File) { + OV.ReadFile (file.fileObject, format, callbacks); + } + } + + GetFileFormat (file) + { + for (let importerIndex = 0; importerIndex < this.importers.length; importerIndex++) { + let importer = this.importers[importerIndex]; + let extension = file.extension.toLowerCase (); + let knownFormats = importer.GetKnownFileFormats (); + if (knownFormats[extension] !== undefined) { + return knownFormats[extension]; + } + } + return OV.FileFormat.Binary; + } + + FindImporter (file) + { + for (let importerIndex = 0; importerIndex < this.importers.length; importerIndex++) { + let importer = this.importers[importerIndex]; + if (importer.CanImportExtension (file.extension.toLowerCase ())) { + return importer; + } + } + return null; + } +}; + +OV.ImporterErrorCode = +{ + NoImportableFile : 1, + ImportFailed : 2, + UnknownError : 3 +}; + +OV.ImporterError = class +{ + constructor (code, message) + { + this.code = code; + this.message = message; + } +}; + +OV.ImportResult = class +{ + constructor () + { + this.model = null; + this.mainFile = null; + this.upVector = null; + this.usedFiles = []; + this.missingFiles = []; + } +}; + +OV.Importer = class +{ + constructor () + { + this.importers = [ + new OV.ImporterObj (), + new OV.ImporterStl (), + new OV.ImporterOff (), + new OV.ImporterPly (), + new OV.Importer3ds (), + new OV.ImporterGltf () + ]; + this.fileList = new OV.FileList (this.importers); + this.missingFiles = []; + } + + LoadFilesFromUrls (fileList, onReady) + { + this.LoadFiles (fileList, OV.FileSource.Url, onReady); + } + + LoadFilesFromFileObjects (fileList, onReady) + { + this.LoadFiles (fileList, OV.FileSource.File, onReady); + } + + Import (callbacks) + { + let mainFile = this.fileList.GetMainFile (); + if (mainFile === null || mainFile.file.content === null) { + callbacks.error (new OV.ImporterError (OV.ImporterErrorCode.NoImportableFile, null)); + return; + } + + let result = new OV.ImportResult (); + result.mainFile = mainFile.file.name; + result.usedFiles.push (mainFile.file.name); + + let fileNameToContent = {}; + let textureNameToUrl = {}; + + let obj = this; + let importer = mainFile.importer; + importer.Import (mainFile.file.content, mainFile.file.extension, { + getDefaultMaterial : function () { + let material = new OV.Material (); + material.diffuse = new OV.Color (200, 200, 200); + return material; + }, + getFileContent : function (filePath) { + let fileName = OV.GetFileName (filePath); + let fileContent = fileNameToContent[fileName]; + if (fileContent === undefined) { + let file = obj.fileList.FindFileByPath (filePath); + if (file === null || file.content === null) { + result.missingFiles.push (fileName); + obj.missingFiles.push (fileName); + fileContent = null; + } else { + result.usedFiles.push (fileName); + fileContent = file.content; + } + fileNameToContent[fileName] = fileContent; + } + return fileContent; + }, + getTextureObjectUrl : function (filePath) { + let fileName = OV.GetFileName (filePath); + let textureUrl = textureNameToUrl[fileName]; + if (textureUrl === undefined) { + let file = obj.fileList.FindFileByPath (filePath); + if (file === null || file.content === null) { + result.missingFiles.push (fileName); + obj.missingFiles.push (fileName); + textureUrl = null; + } else { + result.usedFiles.push (fileName); + let blob = new Blob ([file.content]); + let blobURL = URL.createObjectURL (blob); + textureUrl = blobURL; + } + textureNameToUrl[fileName] = textureUrl; + } + return textureUrl; + } + }); + + if (importer.IsError ()) { + let message = importer.GetMessage (); + callbacks.error (new OV.ImporterError (OV.ImporterErrorCode.ImportFailed, message)); + return; + } + + result.model = importer.GetModel (); + result.model.SetName (mainFile.file.name); + result.upVector = importer.GetUpDirection (); + callbacks.success (result); + } + + LoadFiles (fileList, fileSource, onReady) + { + let newFileList = new OV.FileList (this.importers); + if (fileSource === OV.FileSource.Url) { + newFileList.FillFromFileUrls (fileList); + } else if (fileSource === OV.FileSource.File) { + newFileList.FillFromFileObjects (fileList); + } + let reset = false; + if (newFileList.HasMainFile ()) { + reset = true; + } else { + let foundMissingFile = false; + for (let i = 0; i < this.missingFiles.length; i++) { + let missingFile = this.missingFiles[i]; + if (newFileList.ContainsFileByPath (missingFile)) { + foundMissingFile = true; + } + } + if (!foundMissingFile) { + reset = true; + } else { + let newFiles = newFileList.GetFiles (); + this.fileList.ExtendFromFileList (newFiles); + reset = false; + } + } + if (reset) { + this.fileList = newFileList; + } + this.missingFiles = []; + this.fileList.GetContent (function () { + onReady (); + }); + } + + GetFileList () + { + return this.fileList; + } + + IsOnlyFileSource (source) + { + return this.fileList.IsOnlySource (source); + } +}; diff --git a/source/import/importer3ds.js b/source/import/importer3ds.js new file mode 100644 index 0000000..cdde82b --- /dev/null +++ b/source/import/importer3ds.js @@ -0,0 +1,679 @@ +OV.CHUNK3DS = +{ + MAIN3DS : 0x4D4D, + EDIT3DS : 0x3D3D, + EDIT_MATERIAL : 0xAFFF, + MAT_NAME : 0xA000, + MAT_AMBIENT : 0xA010, + MAT_DIFFUSE : 0xA020, + MAT_SPECULAR : 0xA030, + MAT_SHININESS : 0xA040, + MAT_SHININESS_STRENGTH : 0xA041, + MAT_TRANSPARENCY : 0xA050, + MAT_COLOR_F : 0x0010, + MAT_COLOR : 0x0011, + MAT_LIN_COLOR : 0x0012, + MAT_LIN_COLOR_F : 0x0013, + MAT_TEXMAP : 0xA200, + MAT_TEXMAP_NAME : 0xA300, + MAT_TEXMAP_UOFFSET : 0xA358, + MAT_TEXMAP_VOFFSET : 0xA35A, + MAT_TEXMAP_USCALE : 0xA354, + MAT_TEXMAP_VSCALE : 0xA356, + MAT_TEXMAP_ROTATION : 0xA35C, + PERCENTAGE : 0x0030, + PERCENTAGE_F : 0x0031, + EDIT_OBJECT : 0x4000, + OBJ_TRIMESH : 0x4100, + OBJ_LIGHT : 0x4600, + OBJ_CAMERA : 0x4700, + TRI_VERTEX : 0x4110, + TRI_TEXVERTEX : 0x4140, + TRI_FACE : 0x4120, + TRI_TRANSFORMATION : 0x4160, + TRI_MATERIAL : 0x4130, + TRI_SMOOTH : 0x4150, + KF3DS : 0xB000, + OBJECT_NODE : 0xB002, + OBJECT_HIERARCHY : 0xB010, + OBJECT_INSTANCE_NAME : 0xB011, + OBJECT_PIVOT : 0xB013, + OBJECT_POSITION : 0xB020, + OBJECT_ROTATION : 0xB021, + OBJECT_SCALE : 0xB022, + OBJECT_ID : 0xB030 +}; + +OV.Importer3ds = class extends OV.ImporterBase +{ + constructor () + { + super (); + + this.materialNameToIndex = null; + this.meshNameToIndex = null; + + this.meshTransformations = null; + this.defaultMaterialIndex = null; + } + + ResetState () + { + this.materialNameToIndex = {}; + this.meshNameToIndex = {}; + + this.meshTransformations = []; + this.defaultMaterialIndex = null; + } + + CanImportExtension (extension) + { + return extension === '3ds'; + } + + GetKnownFileFormats () + { + return { + '3ds' : OV.FileFormat.Binary + }; + } + + GetUpDirection () + { + return OV.Direction.Z; + } + + ImportContent (fileContent) + { + this.ProcessBinary (fileContent); + } + + ProcessBinary (fileContent) + { + let obj = this; + let reader = new OV.BinaryReader (fileContent, true); + let endByte = reader.GetByteLength (); + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.MAIN3DS) { + obj.ReadMainChunk (reader, chunkLength); + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + } + + ReadMainChunk (reader, length) + { + let obj = this; + let endByte = this.GetChunkEnd (reader, length); + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.EDIT3DS) { + obj.ReadEditorChunk (reader, chunkLength); + } else if (chunkId === OV.CHUNK3DS.KF3DS) { + obj.ReadKeyFrameChunk (reader, chunkLength); + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + } + + ReadEditorChunk (reader, length) + { + let obj = this; + let endByte = this.GetChunkEnd (reader, length); + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.EDIT_MATERIAL) { + obj.ReadMaterialChunk (reader, chunkLength); + } else if (chunkId === OV.CHUNK3DS.EDIT_OBJECT) { + obj.ReadObjectChunk (reader, chunkLength); + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + } + + ReadMaterialChunk (reader, length) + { + let obj = this; + let material = new OV.Material (); + let endByte = this.GetChunkEnd (reader, length); + let shininess = null; + let shininessStrength = null; + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.MAT_NAME) { + material.name = obj.ReadName (reader); + } else if (chunkId === OV.CHUNK3DS.MAT_AMBIENT) { + material.ambient = obj.ReadColorChunk (reader, chunkLength); + } else if (chunkId === OV.CHUNK3DS.MAT_DIFFUSE) { + material.diffuse = obj.ReadColorChunk (reader, chunkLength); + } else if (chunkId === OV.CHUNK3DS.MAT_SPECULAR) { + material.specular = obj.ReadColorChunk (reader, chunkLength); + } else if (chunkId === OV.CHUNK3DS.MAT_SHININESS) { + shininess = obj.ReadPercentageChunk (reader, chunkLength); + } else if (chunkId === OV.CHUNK3DS.MAT_SHININESS_STRENGTH) { + shininessStrength = obj.ReadPercentageChunk (reader, chunkLength); + } else if (chunkId === OV.CHUNK3DS.MAT_TRANSPARENCY) { + material.opacity = 1.0 - obj.ReadPercentageChunk (reader, chunkLength); + material.transparent = OV.IsLower (material.opacity, 1.0); + } else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP) { + material.diffuseMap = obj.ReadTextureMapChunk (reader, chunkLength); + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + + if (shininess !== null || shininessStrength !== null) { + material.shininess = shininess * shininessStrength; + } + let materialIndex = this.model.AddMaterial (material); + this.materialNameToIndex[material.name] = materialIndex; + } + + ReadTextureMapChunk (reader, length) + { + let obj = this; + let texture = new OV.TextureMap (); + let endByte = this.GetChunkEnd (reader, length); + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_NAME) { + let textureName = obj.ReadName (reader); + let textureObjectUrl = obj.callbacks.getTextureObjectUrl (textureName); + texture.name = textureName; + texture.url = textureObjectUrl; + } else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_UOFFSET) { + texture.offset.x = reader.ReadFloat32 (); + } else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_VOFFSET) { + texture.offset.y = reader.ReadFloat32 (); + } else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_USCALE) { + texture.scale.x = reader.ReadFloat32 (); + } else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_VSCALE) { + texture.scale.y = reader.ReadFloat32 (); + } else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_ROTATION) { + texture.rotation = reader.ReadFloat32 () * OV.DegRad; + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + return texture; + } + + ReadColorChunk (reader, length) + { + let obj = this; + let color = new OV.Color (0, 0, 0); + let endByte = this.GetChunkEnd (reader, length); + let hasLinColor = false; + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.MAT_COLOR) { + if (!hasLinColor) { + color.r = reader.ReadUnsignedCharacter8 (); + color.g = reader.ReadUnsignedCharacter8 (); + color.b = reader.ReadUnsignedCharacter8 (); + } + } else if (chunkId === OV.CHUNK3DS.MAT_LIN_COLOR) { + color.r = reader.ReadUnsignedCharacter8 (); + color.g = reader.ReadUnsignedCharacter8 (); + color.b = reader.ReadUnsignedCharacter8 (); + hasLinColor = true; + } else if (chunkId === OV.CHUNK3DS.MAT_COLOR_F) { + if (!hasLinColor) { + color.r = parseInt (reader.ReadFloat32 () * 255.0, 10); + color.g = parseInt (reader.ReadFloat32 () * 255.0, 10); + color.b = parseInt (reader.ReadFloat32 () * 255.0, 10); + } + } else if (chunkId === OV.CHUNK3DS.MAT_LIN_COLOR_F) { + color.r = parseInt (reader.ReadFloat32 () * 255.0, 10); + color.g = parseInt (reader.ReadFloat32 () * 255.0, 10); + color.b = parseInt (reader.ReadFloat32 () * 255.0, 10); + hasLinColor = true; + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + return color; + } + + ReadPercentageChunk (reader, length) + { + let obj = this; + let percentage = 0.0; + let endByte = this.GetChunkEnd (reader, length); + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.PERCENTAGE) { + percentage = reader.ReadUnsignedInteger16 () / 100.0; + } else if (chunkId === OV.CHUNK3DS.PERCENTAGE_F) { + percentage = reader.ReadFloat32 (); + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + return percentage; + } + + ReadObjectChunk (reader, length) + { + let obj = this; + let endByte = this.GetChunkEnd (reader, length); + let objectName = this.ReadName (reader); + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.OBJ_TRIMESH) { + obj.ReadMeshChunk (reader, chunkLength, objectName); + } else if (chunkId === OV.CHUNK3DS.OBJ_LIGHT) { + obj.SkipChunk (reader, chunkLength); + } else if (chunkId === OV.CHUNK3DS.OBJ_CAMERA) { + obj.SkipChunk (reader, chunkLength); + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + } + + ReadMeshChunk (reader, length, objectName) + { + let obj = this; + + let mesh = new OV.Mesh (); + mesh.SetName (objectName); + + let endByte = this.GetChunkEnd (reader, length); + let transformation = null; + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.TRI_VERTEX) { + obj.ReadVerticesChunk (mesh, reader); + } else if (chunkId === OV.CHUNK3DS.TRI_TEXVERTEX) { + obj.ReadTextureVerticesChunk (mesh, reader); + } else if (chunkId === OV.CHUNK3DS.TRI_FACE) { + obj.ReadFacesChunk (mesh, reader, chunkLength); + } else if (chunkId === OV.CHUNK3DS.TRI_TRANSFORMATION) { + transformation = obj.ReadTransformationChunk (reader); + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + + if (mesh.VertexCount () === mesh.TextureUVCount ()) { + for (let i = 0; i < mesh.TriangleCount (); i++) { + let triangle = mesh.GetTriangle (i); + triangle.SetTextureUVs ( + triangle.v0, + triangle.v1, + triangle.v2 + ); + } + } + + let meshIndex = this.model.AddMesh (mesh); + this.meshNameToIndex[mesh.GetName ()] = meshIndex; + this.meshTransformations.push (new OV.Matrix (transformation)); + } + + ReadVerticesChunk (mesh, reader) + { + let vertexCount = reader.ReadUnsignedInteger16 (); + for (let i = 0; i < vertexCount; i++) { + let x = reader.ReadFloat32 (); + let y = reader.ReadFloat32 (); + let z = reader.ReadFloat32 (); + mesh.AddVertex (new OV.Coord3D (x, y, z)); + } + } + + ReadTextureVerticesChunk (mesh, reader) + { + let texVertexCount = reader.ReadUnsignedInteger16 (); + for (let i = 0; i < texVertexCount; i++) { + let x = reader.ReadFloat32 (); + let y = reader.ReadFloat32 (); + mesh.AddTextureUV (new OV.Coord2D (x, y)); + } + } + + ReadFacesChunk (mesh, reader, length) + { + let endByte = this.GetChunkEnd (reader, length); + let faceCount = reader.ReadUnsignedInteger16 (); + for (let i = 0; i < faceCount; i++) { + let v0 = reader.ReadUnsignedInteger16 (); + let v1 = reader.ReadUnsignedInteger16 (); + let v2 = reader.ReadUnsignedInteger16 (); + let flags = reader.ReadUnsignedInteger16 (); + mesh.AddTriangle (new OV.Triangle (v0, v1, v2)); + } + + let obj = this; + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.TRI_MATERIAL) { + obj.ReadFaceMaterialsChunk (mesh, reader); + } else if (chunkId === OV.CHUNK3DS.TRI_SMOOTH) { + obj.ReadFaceSmoothingGroupsChunk (mesh, faceCount, reader); + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + } + + ReadFaceMaterialsChunk (mesh, reader) + { + let materialName = this.ReadName (reader); + let materialIndex = this.materialNameToIndex[materialName]; + let faceCount = reader.ReadUnsignedInteger16 (); + for (let i = 0; i < faceCount; i++) { + let faceIndex = reader.ReadUnsignedInteger16 (); + let triangle = mesh.GetTriangle (faceIndex); + if (materialIndex !== undefined) { + triangle.mat = materialIndex; + } + } + } + + ReadFaceSmoothingGroupsChunk (mesh, faceCount, reader) + { + for (let i = 0; i < faceCount; i++) { + let smoothingGroup = reader.ReadUnsignedInteger32 (); + let triangle = mesh.GetTriangle (i); + triangle.curve = smoothingGroup; + } + } + + ReadTransformationChunk (reader) + { + let matrix = []; + for (let i = 0; i < 4; i++) { + for (let j = 0; j < 3; j++) { + matrix.push (reader.ReadFloat32 ()); + } + if (i < 3) { + matrix.push (0); + } else { + matrix.push (1); + } + } + return matrix; + } + + ReadKeyFrameChunk (reader, length) + { + let nodeHierarchy = { + nodes : [], + idToIndex : {}, + meshIndexToNodes : {} + }; + + let endByte = this.GetChunkEnd (reader, length); + let obj = this; + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.OBJECT_NODE) { + obj.ReadObjectNodeChunk (nodeHierarchy, reader, chunkLength); + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + + this.ApplyModelTransformations (nodeHierarchy); + } + + ApplyModelTransformations (nodeHierarchy) + { + function GetNodeTransformation (nodeHierarchy, node) + { + function GetNodePosition (node) + { + if (node.positions.length === 0) { + return [0.0, 0.0, 0.0]; + } + return node.positions[0]; + } + + function GetNodeRotation (node) + { + function GetQuaternionFromAxisAndAngle (rotation) + { + let result = [0.0, 0.0, 0.0, 1.0]; + let length = Math.sqrt (rotation[0] * rotation[0] + rotation[1] * rotation[1] + rotation[2] * rotation[2]); + if (length > 0.0) { + let omega = rotation[3] * -0.5; + let si = Math.sin (omega) / length; + result = [si * rotation[0], si * rotation[1], si * rotation[2], Math.cos (omega)]; + } + return result; + } + + if (node.rotations.length === 0) { + return [0.0, 0.0, 0.0, 1.0]; + } + + let rotation = node.rotations[0]; + return GetQuaternionFromAxisAndAngle (rotation); + } + + function GetNodeScale (node) + { + if (node.scales.length === 0) { + return [1.0, 1.0, 1.0]; + } + return node.scales[0]; + } + + if (node.matrix !== null) { + return node.matrix; + } + + let matrix = new OV.Matrix (); + matrix.ComposeTRS ( + GetNodePosition (node), + GetNodeRotation (node), + GetNodeScale (node) + ); + + if (node.userId !== 65535) { + let parentIndex = nodeHierarchy.idToIndex[node.userId]; + if (parentIndex !== undefined) { + let parentNode = nodeHierarchy.nodes[parentIndex]; + let parentMatrix = GetNodeTransformation (nodeHierarchy, parentNode); + matrix = matrix.MultiplyMatrix (parentMatrix); + } + } + + node.matrix = matrix; + return matrix; + } + + function ApplyMeshTransformation (model, currentMeshIndex, meshMatrix, nodeHierarchy, node) + { + function GetNodePivotPoint (node) + { + if (node === null) { + return [0.0, 0.0, 0.0]; + } + return node.pivot; + } + + if (!meshMatrix.IsValid ()) { + return; + } + + let nodeMatrix = meshMatrix; + if (node !== null) { + nodeMatrix = GetNodeTransformation (nodeHierarchy, node); + } + + let determinant = meshMatrix.Determinant (); + if (OV.IsNegative (determinant)) { + // Mirror by x coordinates + let scaleMatrix = new OV.Matrix ().CreateScale (-1.0, 1.0, 1.0); + meshMatrix = scaleMatrix.MultiplyMatrix (meshMatrix); + } + + let invMeshMatrix = meshMatrix.Invert (); + if (invMeshMatrix === null) { + return; + } + + let pivotPoint = GetNodePivotPoint (node); + let pivotMatrix = new OV.Matrix ().CreateTranslation (-pivotPoint[0], -pivotPoint[1], -pivotPoint[2]); + + let matrix = nodeMatrix.Clone (); + matrix = pivotMatrix.MultiplyMatrix (matrix); + matrix = invMeshMatrix.MultiplyMatrix (matrix); + + let transformation = new OV.Transformation (matrix); + let mesh = model.GetMesh (currentMeshIndex); + OV.TransformMesh (mesh, transformation); + } + + function AddDuplicatedMesh (model, meshIndex, toIndex) + { + let mesh = model.GetMesh (meshIndex); + let clonedMesh = OV.CloneMesh (mesh); + let clonedMeshIndex = model.AddMeshToIndex (clonedMesh, toIndex); + return clonedMeshIndex; + } + + let newToOldMeshIndexOffset = 0; + for (let meshIndex = 0; meshIndex < this.model.MeshCount (); meshIndex++) { + let currentMeshIndex = meshIndex; + let originalMeshIndex = currentMeshIndex - newToOldMeshIndexOffset; + let meshTransformation = this.meshTransformations[originalMeshIndex]; + let meshNodes = nodeHierarchy.meshIndexToNodes[originalMeshIndex]; + if (meshNodes === undefined) { + ApplyMeshTransformation (this.model, currentMeshIndex, meshTransformation, nodeHierarchy, null); + } else { + for (let nodeIndex = 0; nodeIndex < meshNodes.length; nodeIndex++) { + let currentNode = meshNodes[nodeIndex]; + let transformedMeshIndex = currentMeshIndex; + if (nodeIndex > 0) { + transformedMeshIndex = AddDuplicatedMesh (this.model, currentMeshIndex, currentMeshIndex + nodeIndex); + newToOldMeshIndexOffset += 1; + meshIndex += 1; + } + ApplyMeshTransformation (this.model, transformedMeshIndex, meshTransformation, nodeHierarchy, currentNode); + } + } + } + } + + ReadObjectNodeChunk (nodeHierarchy, reader, length) + { + function ReadTrackVector (obj, reader, type) + { + let result = []; + reader.Skip (10); + + let keyNum = reader.ReadInteger32 (); + for (let i = 0; i < keyNum; i++) { + reader.ReadInteger32 (); + let flags = reader.ReadUnsignedInteger16 (); + if (flags !== 0) { + reader.ReadFloat32 (); + } + + let current = null; + if (type === OV.CHUNK3DS.OBJECT_ROTATION) { + let tmp = reader.ReadFloat32 (); + current = obj.ReadVector (reader); + current[3] = tmp; + } else { + current = obj.ReadVector (reader); + } + result.push (current); + } + + return result; + } + + let objectNode = { + name : '', + instanceName : '', + nodeId : -1, + flags : -1, + userId : -1, + pivot : [0.0, 0.0, 0.0], + positions : [], + rotations : [], + scales : [], + matrix : null + }; + + let obj = this; + let endByte = this.GetChunkEnd (reader, length); + this.ReadChunks (reader, endByte, function (chunkId, chunkLength) { + if (chunkId === OV.CHUNK3DS.OBJECT_HIERARCHY) { + objectNode.name = obj.ReadName (reader); + objectNode.flags = reader.ReadUnsignedInteger32 (); + objectNode.userId = reader.ReadUnsignedInteger16 (); + } else if (chunkId === OV.CHUNK3DS.OBJECT_INSTANCE_NAME) { + objectNode.instanceName = obj.ReadName (reader); + } else if (chunkId === OV.CHUNK3DS.OBJECT_PIVOT) { + objectNode.pivot = obj.ReadVector (reader); + } else if (chunkId === OV.CHUNK3DS.OBJECT_POSITION) { + objectNode.positions = ReadTrackVector (obj, reader, OV.CHUNK3DS.OBJECT_POSITION); + } else if (chunkId === OV.CHUNK3DS.OBJECT_ROTATION) { + objectNode.rotations = ReadTrackVector (obj, reader, OV.CHUNK3DS.OBJECT_ROTATION); + } else if (chunkId === OV.CHUNK3DS.OBJECT_SCALE) { + objectNode.scales = ReadTrackVector (obj, reader, OV.CHUNK3DS.OBJECT_SCALE); + } else if (chunkId === OV.CHUNK3DS.OBJECT_ID) { + objectNode.nodeId = reader.ReadUnsignedInteger16 (); + } else { + obj.SkipChunk (reader, chunkLength); + } + }); + + let nodeIndex = nodeHierarchy.nodes.length; + nodeHierarchy.nodes.push (objectNode); + nodeHierarchy.idToIndex[objectNode.nodeId] = nodeIndex; + + let meshIndex = this.meshNameToIndex[objectNode.name]; + if (meshIndex !== undefined) { + let meshNodes = nodeHierarchy.meshIndexToNodes[meshIndex]; + if (meshNodes === undefined) { + nodeHierarchy.meshIndexToNodes[meshIndex] = []; + } + nodeHierarchy.meshIndexToNodes[meshIndex].push (objectNode); + } + } + + ReadName (reader) + { + let name = ''; + let char = 0; + let count = 0; + while (count < 64) { + char = reader.ReadCharacter8 (); + if (char === 0) { + break; + } + name = name + String.fromCharCode (char); + count = count + 1; + } + return name; + } + + ReadVector (reader) + { + let result = [ + reader.ReadFloat32 (), + reader.ReadFloat32 (), + reader.ReadFloat32 () + ]; + return result; + } + + ReadChunks (reader, endByte, onChunk) + { + while (reader.GetPosition () <= endByte - 6) { + let chunkId = reader.ReadUnsignedInteger16 (); + let chunkLength = reader.ReadUnsignedInteger32 (); + onChunk (chunkId, chunkLength); + } + } + + GetChunkEnd (reader, length) + { + return reader.GetPosition () + length - 6; + } + + SkipChunk (reader, length) + { + reader.Skip (length - 6); + } +}; diff --git a/source/import/importerbase.js b/source/import/importerbase.js new file mode 100644 index 0000000..3729961 --- /dev/null +++ b/source/import/importerbase.js @@ -0,0 +1,83 @@ +OV.ImporterBase = class +{ + constructor () + { + this.extension = null; + this.callbacks = null; + this.model = null; + this.error = null; + this.message = null; + } + + Import (content, extension, callbacks) + { + this.extension = extension; + this.callbacks = callbacks; + this.model = new OV.Model (); + this.error = false; + this.message = null; + + this.ResetState (); + this.ImportContent (content); + if (this.error) { + return; + } + + if (OV.IsModelEmpty (this.model)) { + this.error = true; + return; + } + + OV.FinalizeModel (this.model, this.callbacks.getDefaultMaterial); + } + + ResetState () + { + + } + + ImportContent (content) + { + + } + + CanImportExtension (extension) + { + return false; + } + + GetKnownFileFormats () + { + return {}; + } + + GetUpDirection () + { + return OV.Direction.Z; + } + + GetModel () + { + return this.model; + } + + SetError () + { + this.error = true; + } + + IsError () + { + return this.error; + } + + SetMessage (message) + { + this.message = message; + } + + GetMessage () + { + return this.message; + } +}; diff --git a/source/import/importergltf.js b/source/import/importergltf.js new file mode 100644 index 0000000..f8d5017 --- /dev/null +++ b/source/import/importergltf.js @@ -0,0 +1,899 @@ +OV.GltfComponentType = +{ + BYTE : 5120, + UNSIGNED_BYTE : 5121, + SHORT : 5122, + UNSIGNED_SHORT : 5123, + UNSIGNED_INT : 5125, + FLOAT : 5126 +}; + +OV.GltfDataType = +{ + SCALAR : 0, + VEC2 : 1, + VEC3 : 2, + VEC4 : 3, + MAT2 : 4, + MAT3 : 5, + MAT4 : 6 +}; + +OV.GltfRenderMode = +{ + POINTS : 0, + LINES : 1, + LINE_LOOP : 2, + LINE_STRIP : 3, + TRIANGLES : 4, + TRIANGLE_STRIP : 5, + TRIANGLE_FAN : 6 +}; + +OV.GltfConstants = +{ + GLTF_STRING : 0x46546C67, + JSON_CHUNK_TYPE : 0x4E4F534A, + BINARY_CHUNK_TYPE : 0x004E4942 +}; + +OV.GltfNodeTree = class +{ + constructor () + { + this.nodes = []; + this.nodeToParent = {}; + this.nodeMatrices = {}; + } + + AddMeshNode (nodeIndex) + { + this.nodes.push (nodeIndex); + return this.nodes.length - 1; + } + + AddNodeParent (nodeIndex, parentIndex) + { + this.nodeToParent[nodeIndex] = parentIndex; + } + + GetNodeParent (nodeIndex) + { + let parentIndex = this.nodeToParent[nodeIndex]; + if (parentIndex === undefined || parentIndex === -1) { + return null; + } + return parentIndex; + } + + AddNodeMatrix (nodeIndex, matrix) + { + this.nodeMatrices[nodeIndex] = matrix; + } + + GetNodeMatrix (nodeIndex) + { + let matrix = this.nodeMatrices[nodeIndex]; + if (matrix === undefined) { + return null; + } + return matrix; + } +}; + +OV.GltfBufferReader = class +{ + constructor (buffer) + { + this.reader = new OV.BinaryReader (buffer, true); + this.componentType = null; + this.dataType = null; + this.byteStride = null; + this.dataCount = null; + this.sparseReader = null; + } + + SetComponentType (componentType) + { + this.componentType = componentType; + } + + SetDataType (dataType) + { + if (dataType === 'SCALAR') { + this.dataType = OV.GltfDataType.SCALAR; + } else if (dataType === 'VEC2') { + this.dataType = OV.GltfDataType.VEC2; + } else if (dataType === 'VEC3') { + this.dataType = OV.GltfDataType.VEC3; + } else if (dataType === 'VEC4') { + this.dataType = OV.GltfDataType.VEC4; + } else if (dataType === 'MAT2') { + this.dataType = OV.GltfDataType.MAT2; + } else if (dataType === 'MAT3') { + this.dataType = OV.GltfDataType.MAT3; + } else if (dataType === 'MAT4') { + this.dataType = OV.GltfDataType.MAT4; + } + } + + SetByteStride (byteStride) + { + this.byteStride = byteStride; + } + + SetDataCount (dataCount) + { + this.dataCount = dataCount; + } + + SetSparseReader (indexReader, valueReader) + { + this.sparseReader = { + indexReader : indexReader, + valueReader : valueReader + }; + } + + ReadArrayBuffer (byteLength) + { + return this.reader.ReadArrayBuffer (byteLength); + } + + GetDataCount () + { + return this.dataCount; + } + + ReadData () + { + if (this.dataType === null) { + return null; + } + if (this.dataType === OV.GltfDataType.SCALAR) { + let data = this.ReadComponent (); + this.SkipBytesByStride (1); + return data; + } else if (this.dataType === OV.GltfDataType.VEC2) { + let x = this.ReadComponent (); + let y = this.ReadComponent (); + this.SkipBytesByStride (2); + return new OV.Coord2D (x, y); + } else if (this.dataType === OV.GltfDataType.VEC3) { + let x = this.ReadComponent (); + let y = this.ReadComponent (); + let z = this.ReadComponent (); + this.SkipBytesByStride (3); + return new OV.Coord3D (x, y, z); + } + return null; + } + + EnumerateData (onData) + { + if (this.sparseReader === null) { + for (let i = 0; i < this.dataCount; i++) { + onData (this.ReadData ()); + } + } else { + let sparseData = []; + for (let i = 0; i < this.sparseReader.indexReader.GetDataCount (); i++) { + let index = this.sparseReader.indexReader.ReadData (); + let value = this.sparseReader.valueReader.ReadData (); + sparseData.push ({ + index : index, + value : value + }); + } + let sparseIndex = 0; + for (let i = 0; i < this.dataCount; i++) { + let data = this.ReadData (); + if (sparseIndex < sparseData.length && sparseData[sparseIndex].index === i) { + onData (sparseData[sparseIndex].value); + sparseIndex += 1; + } else { + onData (data); + } + } + } + } + + SkipBytes (bytes) + { + this.reader.Skip (bytes); + } + + ReadComponent () + { + if (this.componentType === null) { + return null; + } + if (this.componentType === OV.GltfComponentType.BYTE) { + return this.reader.ReadCharacter8 (); + } else if (this.componentType === OV.GltfComponentType.UNSIGNED_BYTE) { + return this.reader.ReadUnsignedCharacter8 (); + } else if (this.componentType === OV.GltfComponentType.SHORT) { + return this.reader.ReadInteger16 (); + } else if (this.componentType === OV.GltfComponentType.UNSIGNED_SHORT) { + return this.reader.ReadUnsignedInteger16 (); + } else if (this.componentType === OV.GltfComponentType.UNSIGNED_INT) { + return this.reader.ReadInteger32 (); + } else if (this.componentType === OV.GltfComponentType.FLOAT) { + return this.reader.ReadFloat32 (); + } + return null; + } + + SkipBytesByStride (componentCount) + { + if (this.byteStride === null) { + return; + } + let readBytes = componentCount * this.GetComponentSize (); + this.reader.Skip (this.byteStride - readBytes); + } + + GetComponentSize () + { + if (this.componentType === OV.GltfComponentType.BYTE) { + return 1; + } else if (this.componentType === OV.GltfComponentType.UNSIGNED_BYTE) { + return 1; + } else if (this.componentType === OV.GltfComponentType.SHORT) { + return 2; + } else if (this.componentType === OV.GltfComponentType.UNSIGNED_SHORT) { + return 2; + } else if (this.componentType === OV.GltfComponentType.UNSIGNED_INT) { + return 4; + } else if (this.componentType === OV.GltfComponentType.FLOAT) { + return 4; + } + return 0; + } +}; + +OV.GltfExtensions = class +{ + constructor () + { + this.supportedExtensions = [ + 'KHR_materials_pbrSpecularGlossiness', + 'KHR_texture_transform', + ]; + } + + GetUnsupportedExtensions (extensionsRequired) + { + let unsupportedExtensions = []; + if (extensionsRequired === undefined) { + return unsupportedExtensions; + } + for (let i = 0; i < extensionsRequired.length; i++) { + let requiredExtension = extensionsRequired[i]; + if (this.supportedExtensions.indexOf (requiredExtension) === -1) { + unsupportedExtensions.push (requiredExtension); + } + } + return unsupportedExtensions; + } + + ProcessMaterial (gltfMaterial, material, imporTextureFn) + { + function GetMaterialComponent (component) + { + return parseInt (Math.round (OV.LinearToSRGB (component) * 255.0), 10); + } + + if (gltfMaterial.extensions === undefined) { + return; + } + let khrSpecularGlossiness = gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness; + if (khrSpecularGlossiness !== undefined) { + let diffuseColor = khrSpecularGlossiness.diffuseFactor; + if (diffuseColor !== undefined) { + material.diffuse = new OV.Color ( + GetMaterialComponent (diffuseColor[0]), + GetMaterialComponent (diffuseColor[1]), + GetMaterialComponent (diffuseColor[2]) + ); + material.opacity = diffuseColor[3]; + } + let diffuseTexture = khrSpecularGlossiness.diffuseTexture; + if (diffuseTexture !== undefined) { + material.diffuseMap = imporTextureFn (diffuseTexture); + } + let specularColor = khrSpecularGlossiness.specularFactor; + if (specularColor !== undefined) { + material.specular = new OV.Color ( + GetMaterialComponent (specularColor[0]), + GetMaterialComponent (specularColor[1]), + GetMaterialComponent (specularColor[2]) + ); + } + let specularTexture = khrSpecularGlossiness.specularGlossinessTexture; + if (specularTexture !== undefined) { + material.specularMap = imporTextureFn (specularTexture); + } + let glossiness = khrSpecularGlossiness.glossinessFactor; + if (glossiness !== undefined) { + material.shininess = glossiness; + } + } + } + + ProcessTexture (gltfTexture, texture) + { + if (gltfTexture.extensions === undefined) { + return; + } + let khrTextureTransform = gltfTexture.extensions.KHR_texture_transform; + if (khrTextureTransform !== undefined) { + if (khrTextureTransform.offset !== undefined) { + texture.offset.x = khrTextureTransform.offset[0]; + texture.offset.y = -khrTextureTransform.offset[1]; + } + if (khrTextureTransform.scale !== undefined) { + texture.scale.x = khrTextureTransform.scale[0]; + texture.scale.y = khrTextureTransform.scale[1]; + } + if (khrTextureTransform.rotation !== undefined) { + texture.rotation = -khrTextureTransform.rotation; + } + } + } +}; + +OV.ImporterGltf = class extends OV.ImporterBase +{ + constructor () + { + super (); + + this.bufferContents = null; + this.gltfExtensions = null; + } + + ResetState () + { + this.bufferContents = []; + this.gltfExtensions = new OV.GltfExtensions (); + } + + CanImportExtension (extension) + { + return extension === 'gltf' || extension === 'glb'; + } + + GetKnownFileFormats () + { + return { + 'gltf' : OV.FileFormat.Text, + 'glb' : OV.FileFormat.Binary, + 'bin' : OV.FileFormat.Binary + }; + } + + GetUpDirection () + { + return OV.Direction.Y; + } + + ImportContent (fileContent) + { + if (this.extension === 'gltf') { + this.ProcessGltf (fileContent); + } else if (this.extension === 'glb') { + this.ProcessBinaryGltf (fileContent); + } + } + + ProcessGltf (fileContent) + { + let gltf = JSON.parse (fileContent); + if (gltf.asset.version !== '2.0') { + this.SetError (); + this.SetMessage ('Invalid glTF version.'); + return; + } + + for (let i = 0; i < gltf.buffers.length; i++) { + let buffer = null; + let gltfBuffer = gltf.buffers[i]; + let base64Buffer = OV.Base64DataURIToArrayBuffer (gltfBuffer.uri); + if (base64Buffer === null) { + buffer = this.callbacks.getFileContent (gltfBuffer.uri); + } else { + buffer = base64Buffer.buffer; + } + if (buffer === null) { + this.SetError (); + this.SetMessage ('One of the requested buffers is missing.'); + return; + } + this.bufferContents.push (buffer); + } + + this.ProcessMainFile (gltf); + } + + ProcessBinaryGltf (fileContent) + { + function ReadChunk (reader) + { + let length = reader.ReadUnsignedInteger32 (); + let type = reader.ReadUnsignedInteger32 (); + let buffer = reader.ReadArrayBuffer (length); + return { + type : type, + buffer : buffer + }; + } + + let reader = new OV.BinaryReader (fileContent, true); + let magic = reader.ReadUnsignedInteger32 (); + if (magic !== OV.GltfConstants.GLTF_STRING) { + this.SetError (); + this.SetMessage ('Invalid glTF file.'); + return; + } + let version = reader.ReadUnsignedInteger32 (); + if (version !== 2) { + this.SetError (); + this.SetMessage ('Invalid glTF version.'); + return; + } + let length = reader.ReadUnsignedInteger32 (); + if (length !== reader.GetByteLength ()) { + this.SetError (); + this.SetMessage ('Invalid glTF file.'); + return; + } + + let gltfTextContent = null; + while (!reader.End ()) { + let chunk = ReadChunk (reader); + if (chunk.type === OV.GltfConstants.JSON_CHUNK_TYPE) { + gltfTextContent = OV.ArrayBufferToUtf8String (chunk.buffer); + } else if (chunk.type === OV.GltfConstants.BINARY_CHUNK_TYPE) { + this.bufferContents.push (chunk.buffer); + } + } + + if (gltfTextContent !== null) { + let gltf = JSON.parse (gltfTextContent); + this.ProcessMainFile (gltf); + } + } + + ProcessMainFile (gltf) + { + let unsupportedExtensions = this.gltfExtensions.GetUnsupportedExtensions (gltf.extensionsRequired); + if (unsupportedExtensions.length > 0) { + this.SetError (); + this.SetMessage ('Unsupported extension: ' + unsupportedExtensions.join (', ') + '.'); + return; + } + + let defaultScene = this.GetDefaultScene (gltf); + if (defaultScene === null) { + this.SetError (); + this.SetMessage ('No default scene found.'); + return; + } + + let materials = gltf.materials; + if (materials !== undefined) { + for (let i = 0; i < materials.length; i++) { + this.ImportMaterial (gltf, i); + } + } + + let nodeTree = this.CollectMeshNodesForScene (gltf, defaultScene); + for (let i = 0; i < nodeTree.nodes.length; i++) { + let nodeIndex = nodeTree.nodes[i]; + this.ImportMeshNode (gltf, nodeIndex, nodeTree); + } + } + + GetDefaultScene (gltf) + { + let defaultSceneIndex = gltf.scene || 0; + if (defaultSceneIndex >= gltf.scenes.length) { + return null; + } + return gltf.scenes[defaultSceneIndex]; + } + + CollectMeshNodesForScene (gltf, scene) + { + function CollectMeshNodeIndices (gltf, parentIndex, nodeIndex, nodeTree, meshNodes) + { + let node = gltf.nodes[nodeIndex]; + if (node.mesh !== undefined) { + nodeTree.AddMeshNode (nodeIndex); + meshNodes.push (nodeIndex); + } + nodeTree.AddNodeParent (nodeIndex, parentIndex); + if (node.children !== undefined) { + for (let i = 0; i < node.children.length; i++) { + let childNodeIndex = node.children[i]; + CollectMeshNodeIndices (gltf, nodeIndex, childNodeIndex, nodeTree, meshNodes); + } + } + } + + let nodeTree = new OV.GltfNodeTree (); + let meshNodes = []; + for (let i = 0; i < scene.nodes.length; i++) { + let nodeIndex = scene.nodes[i]; + CollectMeshNodeIndices (gltf, -1, nodeIndex, nodeTree, meshNodes); + } + return nodeTree; + } + + ImportMaterial (gltf, materialIndex) + { + function GetMaterialComponent (component) + { + return parseInt (Math.round (OV.LinearToSRGB (component) * 255.0), 10); + } + + let gltfMaterial = gltf.materials[materialIndex]; + let material = new OV.Material (); + if (gltfMaterial.name !== undefined) { + material.name = gltfMaterial.name; + } + + material.diffuse = new OV.Color ( + GetMaterialComponent (1.0), + GetMaterialComponent (1.0), + GetMaterialComponent (1.0) + ); + if (gltfMaterial.pbrMetallicRoughness !== undefined) { + let baseColor = gltfMaterial.pbrMetallicRoughness.baseColorFactor; + if (baseColor !== undefined) { + material.diffuse = new OV.Color ( + GetMaterialComponent (baseColor[0]), + GetMaterialComponent (baseColor[1]), + GetMaterialComponent (baseColor[2]) + ); + material.opacity = baseColor[3]; + } + let emissiveColor = gltfMaterial.emissiveFactor; + if (emissiveColor !== undefined) { + material.emissive = new OV.Color ( + GetMaterialComponent (emissiveColor[0]), + GetMaterialComponent (emissiveColor[1]), + GetMaterialComponent (emissiveColor[2]) + ); + } + + material.diffuseMap = this.ImportTexture (gltf, gltfMaterial.pbrMetallicRoughness.baseColorTexture); + material.normalMap = this.ImportTexture (gltf, gltfMaterial.normalTexture); + material.emissiveMap = this.ImportTexture (gltf, gltfMaterial.emissiveTexture); + if (material.diffuseMap !== null) { + material.multiplyDiffuseMap = true; + } + + let alphaMode = gltfMaterial.alphaMode; + if (alphaMode !== undefined) { + if (alphaMode === 'BLEND') { + material.transparent = true; + } else if (alphaMode === 'MASK') { + material.transparent = true; + material.alphaTest = gltfMaterial.alphaCutoff || 0.5; + } + } + } + + let obj = this; + this.gltfExtensions.ProcessMaterial (gltfMaterial, material, function (textureRef) { + return obj.ImportTexture (gltf, textureRef); + }); + this.model.AddMaterial (material); + } + + ImportTexture (gltf, gltfTextureRef) + { + function GetTextureFileExtension (mimeType) + { + if (mimeType === undefined || mimeType === null) { + return ''; + } + let mimeParts = mimeType.split ('/'); + if (mimeParts.length === 0) { + return ''; + } + return '.' + mimeParts[mimeParts.length - 1]; + } + + if (gltfTextureRef === undefined || gltfTextureRef === null) { + return null; + } + + let texture = new OV.TextureMap (); + let gltfTexture = gltf.textures[gltfTextureRef.index]; + let gltfImage = gltf.images[gltfTexture.source]; + + let textureIndexString = gltfTexture.source.toString (); + if (gltfImage.uri !== undefined) { + let base64Buffer = OV.Base64DataURIToArrayBuffer (gltfImage.uri); + if (base64Buffer !== null) { + let blob = new Blob ([base64Buffer.buffer], { type : base64Buffer.mimeType }); + texture.name = 'Embedded_' + textureIndexString + GetTextureFileExtension (base64Buffer.mimeType); + texture.url = URL.createObjectURL (blob); + } else { + let textureObjectUrl = this.callbacks.getTextureObjectUrl (gltfImage.uri); + texture.name = gltfImage.uri; + texture.url = textureObjectUrl; + } + } else if (gltfImage.bufferView !== undefined) { + let bufferView = gltf.bufferViews[gltfImage.bufferView]; + let reader = this.GetReaderFromBufferView (bufferView); + if (reader !== null) { + let buffer = reader.ReadArrayBuffer (bufferView.byteLength); + let blob = new Blob ([buffer], { type: gltfImage.mimeType }); + texture.name = 'Binary_' + textureIndexString + GetTextureFileExtension (gltfImage.mimeType); + texture.url = URL.createObjectURL (blob); + } + } + + this.gltfExtensions.ProcessTexture (gltfTextureRef, texture); + return texture; + } + + ImportMeshNode (gltf, nodeIndex, nodeTree) + { + function GetNodeTransformation (gltf, nodeIndex, nodeTree) + { + let node = gltf.nodes[nodeIndex]; + let matrix = nodeTree.GetNodeMatrix (nodeIndex); + if (matrix !== null) { + return matrix; + } + + matrix = new OV.Matrix ().CreateIdentity (); + if (node.matrix !== undefined) { + matrix.Set (node.matrix); + } else { + let hasTransformation = false; + let translation = [0.0, 0.0, 0.0]; + let rotation = [0.0, 0.0, 0.0, 1.0]; + let scale = [1.0, 1.0, 1.0]; + if (node.translation !== undefined) { + translation = node.translation; + hasTransformation = true; + } + if (node.rotation !== undefined) { + rotation = node.rotation; + hasTransformation = true; + } + if (node.scale !== undefined) { + scale = node.scale; + hasTransformation = true; + } + + if (hasTransformation) { + matrix.ComposeTRS (translation, rotation, scale); + } + } + + let parentNodeIndex = nodeTree.GetNodeParent (nodeIndex); + if (parentNodeIndex !== null) { + let parentMatrix = GetNodeTransformation (gltf, parentNodeIndex, nodeTree); + matrix = matrix.MultiplyMatrix (parentMatrix); + } + + nodeTree.AddNodeMatrix (nodeIndex, matrix); + return matrix; + } + + let gltfNode = gltf.nodes[nodeIndex]; + let gltfMeshIndex = gltfNode.mesh; + let gltfMesh = gltf.meshes[gltfMeshIndex]; + + let mesh = new OV.Mesh (); + this.model.AddMesh (mesh); + if (gltfMesh.name !== undefined) { + mesh.SetName (gltfMesh.name); + } + + for (let i = 0; i < gltfMesh.primitives.length; i++) { + let primitive = gltfMesh.primitives[i]; + this.ImportPrimitive (gltf, primitive, mesh); + } + + let matrix = GetNodeTransformation (gltf, nodeIndex, nodeTree); + let transformation = new OV.Transformation (matrix); + OV.TransformMesh (mesh, transformation); + } + + ImportPrimitive (gltf, primitive, mesh) + { + function AddTriangle (mesh, v0, v1, v2, hasNormals, hasUVs, vertexOffset, normalOffset, uvOffset) + { + let triangle = new OV.Triangle (vertexOffset + v0, vertexOffset + v1, vertexOffset + v2); + if (hasNormals) { + triangle.SetNormals ( + normalOffset + v0, + normalOffset + v1, + normalOffset + v2 + ); + } + if (hasUVs) { + triangle.SetTextureUVs ( + uvOffset + v0, + uvOffset + v1, + uvOffset + v2 + ); + } + if (primitive.material !== undefined) { + triangle.mat = primitive.material; + } + mesh.AddTriangle (triangle); + } + + if (primitive.attributes === undefined) { + return; + } + + let hasVertices = (primitive.attributes.POSITION !== undefined); + let hasNormals = (primitive.attributes.NORMAL !== undefined); + let hasUVs = (primitive.attributes.TEXCOORD_0 !== undefined); + let hasIndices = (primitive.indices !== undefined); + if (!hasVertices) { + return; + } + + let mode = OV.GltfRenderMode.TRIANGLES; + if (primitive.mode !== undefined) { + mode = primitive.mode; + } + if (mode !== OV.GltfRenderMode.TRIANGLES && mode !== OV.GltfRenderMode.TRIANGLE_STRIP && mode !== OV.GltfRenderMode.TRIANGLE_FAN) { + return; + } + + let vertexOffset = mesh.VertexCount (); + let normalOffset = mesh.NormalCount (); + let uvOffset = mesh.TextureUVCount (); + + if (hasVertices) { + let accessor = gltf.accessors[primitive.attributes.POSITION]; + let reader = this.GetReaderFromAccessor (gltf, accessor); + if (reader === null) { + return; + } + reader.EnumerateData (function (data) { + mesh.AddVertex (data); + }); + } + + if (hasNormals) { + let accessor = gltf.accessors[primitive.attributes.NORMAL]; + let reader = this.GetReaderFromAccessor (gltf, accessor); + if (reader === null) { + return; + } + reader.EnumerateData (function (data) { + mesh.AddNormal (data); + }); + } + + if (hasUVs) { + let accessor = gltf.accessors[primitive.attributes.TEXCOORD_0]; + let reader = this.GetReaderFromAccessor (gltf, accessor); + if (reader === null) { + return; + } + reader.EnumerateData (function (data) { + data.y = -data.y; + mesh.AddTextureUV (data); + }); + } + + let vertexIndices = []; + if (hasIndices) { + let accessor = gltf.accessors[primitive.indices]; + let reader = this.GetReaderFromAccessor (gltf, accessor); + if (reader === null) { + return; + } + reader.EnumerateData (function (data) { + vertexIndices.push (data); + }); + } else { + for (let i = 0; i < mesh.VertexCount (); i++) { + vertexIndices.push (i); + } + } + + if (mode === OV.GltfRenderMode.TRIANGLES) { + for (let i = 0; i < vertexIndices.length; i += 3) { + let v0 = vertexIndices[i]; + let v1 = vertexIndices[i + 1]; + let v2 = vertexIndices[i + 2]; + AddTriangle (mesh, v0, v1, v2, hasNormals, hasUVs, vertexOffset, normalOffset, uvOffset); + } + } else if (mode === OV.GltfRenderMode.TRIANGLE_STRIP) { + for (let i = 0; i < vertexIndices.length - 2; i++) { + let v0 = vertexIndices[i]; + let v1 = vertexIndices[i + 1]; + let v2 = vertexIndices[i + 2]; + if (i % 2 === 1) { + let tmp = v1; + v1 = v2; + v2 = tmp; + } + AddTriangle (mesh, v0, v1, v2, hasNormals, hasUVs, vertexOffset, normalOffset, uvOffset); + } + } else if (mode === OV.GltfRenderMode.TRIANGLE_FAN) { + for (let i = 1; i < vertexIndices.length - 1; i++) { + let v0 = vertexIndices[0]; + let v1 = vertexIndices[i]; + let v2 = vertexIndices[i + 1]; + AddTriangle (mesh, v0, v1, v2, hasNormals, hasUVs, vertexOffset, normalOffset, uvOffset); + } + } + } + + GetReaderFromBufferView (bufferView) + { + let bufferIndex = bufferView.buffer || 0; + let buffer = this.bufferContents[bufferIndex]; + if (buffer === null) { + return null; + } + + let reader = new OV.GltfBufferReader (buffer); + reader.SkipBytes (bufferView.byteOffset || 0); + let byteStride = bufferView.byteStride; + if (byteStride !== undefined) { + reader.SetByteStride (byteStride); + } + + return reader; + } + + GetReaderFromAccessor (gltf, accessor) + { + let bufferViewIndex = accessor.bufferView || 0; + let bufferView = gltf.bufferViews[bufferViewIndex]; + let reader = this.GetReaderFromBufferView (bufferView); + if (reader === null) { + return null; + } + + reader.SetComponentType (accessor.componentType); + reader.SetDataType (accessor.type); + reader.SetDataCount (accessor.count); + reader.SkipBytes (accessor.byteOffset || 0); + + if (accessor.sparse !== undefined) { + let indexReader = this.GetReaderFromSparseAccessor (gltf, accessor.sparse.indices, accessor.sparse.indices.componentType, 'SCALAR', accessor.sparse.count); + let valueReader = this.GetReaderFromSparseAccessor (gltf, accessor.sparse.values, accessor.componentType, accessor.type, accessor.sparse.count); + if (indexReader !== null && valueReader !== null) { + reader.SetSparseReader (indexReader, valueReader); + } + } + return reader; + } + + GetReaderFromSparseAccessor (gltf, sparseAccessor, componentType, type, count) + { + if (sparseAccessor.bufferView === undefined) { + return null; + } + + let bufferView = gltf.bufferViews[sparseAccessor.bufferView]; + let reader = this.GetReaderFromBufferView (bufferView); + if (reader === null) { + return null; + } + + reader.SetComponentType (componentType); + reader.SetDataType (type); + reader.SetDataCount (count); + reader.SkipBytes (sparseAccessor.byteOffset || 0); + return reader; + } +}; diff --git a/source/import/importerobj.js b/source/import/importerobj.js new file mode 100644 index 0000000..40b9aff --- /dev/null +++ b/source/import/importerobj.js @@ -0,0 +1,385 @@ +OV.ImporterObj = class extends OV.ImporterBase +{ + constructor () + { + super (); + + this.globalVertices = null; + this.globalNormals = null; + this.globalUvs = null; + + this.currentMesh = null; + this.currentMeshData = null; + this.currentMaterial = null; + this.currentMaterialIndex = null; + + this.meshNameToMeshData = null; + this.materialNameToIndex = null; + } + + ResetState () + { + this.globalVertices = []; + this.globalNormals = []; + this.globalUvs = []; + + this.currentMesh = null; + this.currentMeshData = null; + this.currentMaterial = null; + this.currentMaterialIndex = null; + + this.meshNameToMeshData = {}; + this.materialNameToIndex = {}; + } + + CanImportExtension (extension) + { + return extension === 'obj'; + } + + GetKnownFileFormats () + { + return { + 'obj' : OV.FileFormat.Text, + 'mtl' : OV.FileFormat.Text + }; + } + + GetUpDirection () + { + return OV.Direction.Y; + } + + ImportContent (fileContent) + { + let obj = this; + OV.ReadLines (fileContent, function (line) { + if (!obj.IsError ()) { + obj.ProcessLine (line); + } + }); + } + + ProcessLine (line) + { + if (line[0] === '#') { + return; + } + + let parameters = OV.ParametersFromLine (line, '#'); + if (parameters.length === 0) { + return; + } + + let keyword = parameters[0].toLowerCase (); + parameters.shift (); + + if (this.ProcessMeshParameter (keyword, parameters, line)) { + return; + } + + if (this.ProcessMaterialParameter (keyword, parameters, line)) { + return; + } + } + + AddNewMesh (name) + { + let meshData = this.meshNameToMeshData[name]; + if (meshData === undefined) { + let mesh = new OV.Mesh (); + if (name !== null) { + mesh.SetName (name); + } + this.model.AddMesh (mesh); + meshData = { + mesh : mesh, + data : { + globalToCurrentVertices : {}, + globalToCurrentNormals : {}, + globalToCurrentUvs : {} + } + }; + this.meshNameToMeshData[name] = meshData; + } + this.currentMesh = meshData.mesh; + this.currentMeshData = meshData.data; + } + + ProcessMeshParameter (keyword, parameters, line) + { + if (keyword === 'g' || keyword === 'o') { + if (parameters.length === 0) { + return true; + } + let name = OV.NameFromLine (line, keyword.length, '#'); + this.AddNewMesh (name); + return true; + } else if (keyword === 'v') { + if (parameters.length < 3) { + return true; + } + this.globalVertices.push (new OV.Coord3D ( + parseFloat (parameters[0]), + parseFloat (parameters[1]), + parseFloat (parameters[2]) + )); + return true; + } else if (keyword === 'vn') { + if (parameters.length < 3) { + return true; + } + this.globalNormals.push (new OV.Coord3D ( + parseFloat (parameters[0]), + parseFloat (parameters[1]), + parseFloat (parameters[2]) + )); + return true; + } else if (keyword === 'vt') { + if (parameters.length < 2) { + return true; + } + this.globalUvs.push (new OV.Coord2D ( + parseFloat (parameters[0]), + parseFloat (parameters[1]) + )); + return true; + } else if (keyword === 'f') { + if (parameters.length < 3) { + return true; + } + this.ProcessFace (parameters); + return true; + } + + return false; + } + + ProcessMaterialParameter (keyword, parameters, line) + { + function CreateColor (parameters) + { + return new OV.Color ( + parseInt (parseFloat (parameters[0] * 255.0), 10), + parseInt (parseFloat (parameters[1] * 255.0), 10), + parseInt (parseFloat (parameters[2] * 255.0), 10) + ); + } + + function CreateTexture (keyword, line, callbacks) + { + let texture = new OV.TextureMap (); + texture.name = OV.NameFromLine (line, keyword.length, '#'); + let textureObjectUrl = callbacks.getTextureObjectUrl (texture.name); + if (textureObjectUrl !== null) { + texture.url = textureObjectUrl; + } + return texture; + } + + let obj = this; + if (keyword === 'newmtl') { + if (parameters.length === 0) { + return true; + } + + let material = new OV.Material (); + let materialName = OV.NameFromLine (line, keyword.length, '#'); + let materialIndex = this.model.AddMaterial (material); + material.name = materialName; + this.currentMaterial = material; + this.materialNameToIndex[materialName] = materialIndex; + return true; + } else if (keyword === 'usemtl') { + if (parameters.length === 0) { + return true; + } + + let materialName = OV.NameFromLine (line, keyword.length, '#'); + let materialIndex = this.materialNameToIndex[materialName]; + if (materialIndex !== undefined) { + this.currentMaterialIndex = materialIndex; + } + return true; + } else if (keyword === 'mtllib') { + if (parameters.length === 0) { + return true; + } + let fileName = OV.NameFromLine (line, keyword.length, '#'); + let fileContent = this.callbacks.getFileContent (fileName); + if (fileContent !== null) { + OV.ReadLines (fileContent, function (line) { + if (!obj.IsError ()) { + obj.ProcessLine (line); + } + }); + } + return true; + } else if (keyword === 'map_kd') { + if (this.currentMaterial === null || parameters.length === 0) { + return true; + } + this.currentMaterial.diffuseMap = CreateTexture (keyword, line, this.callbacks); + return true; + } else if (keyword === 'map_ks') { + if (this.currentMaterial === null || parameters.length === 0) { + return true; + } + this.currentMaterial.specularMap = CreateTexture (keyword, line, this.callbacks); + return true; + } else if (keyword === 'map_bump' || keyword === 'bump') { + if (this.currentMaterial === null || parameters.length === 0) { + return true; + } + this.currentMaterial.bumpMap = CreateTexture (keyword, line, this.callbacks); + return true; + } else if (keyword === 'ka') { + if (this.currentMaterial === null || parameters.length < 3) { + return true; + } + this.currentMaterial.ambient = CreateColor (parameters); + return true; + } else if (keyword === 'kd') { + if (this.currentMaterial === null || parameters.length < 3) { + return true; + } + this.currentMaterial.diffuse = CreateColor (parameters); + return true; + } else if (keyword === 'ks') { + if (this.currentMaterial === null || parameters.length < 3) { + return true; + } + this.currentMaterial.specular = CreateColor (parameters); + return true; + } else if (keyword === 'ns') { + if (this.currentMaterial === null || parameters.length < 1) { + return true; + } + this.currentMaterial.shininess = parseFloat (parameters[0]) / 100.0; + return true; + } else if (keyword === 'tr') { + if (this.currentMaterial === null || parameters.length < 1) { + return true; + } + this.currentMaterial.opacity = 1.0 - parseFloat (parameters[0]); + this.currentMaterial.transparent = OV.IsLower (this.currentMaterial.opacity, 1.0); + return true; + } else if (keyword === 'd') { + if (this.currentMaterial === null || parameters.length < 1) { + return true; + } + this.currentMaterial.opacity = parseFloat (parameters[0]); + this.currentMaterial.transparent = OV.IsLower (this.currentMaterial.opacity, 1.0); + return true; + } + + return false; + } + + ProcessFace (parameters) + { + function GetRelativeIndex (index, count) + { + if (index > 0) { + return index - 1; + } else { + return count + index; + } + } + + function GetLocalIndex (globalValueArray, globalToCurrentIndices, globalIndex, valueAdderFunc) + { + if (isNaN (globalIndex) || globalIndex < 0 || globalIndex >= globalValueArray.length) { + return null; + } + let result = globalToCurrentIndices[globalIndex]; + if (result === undefined) { + let globalValue = globalValueArray[globalIndex]; + if (globalValue === undefined) { + return null; + } + result = valueAdderFunc (globalValue); + globalToCurrentIndices[globalIndex] = result; + } + return result; + } + + function GetLocalVertexIndex (obj, mesh, globalIndex) + { + return GetLocalIndex (obj.globalVertices, obj.currentMeshData.globalToCurrentVertices, globalIndex, function (val) { + return mesh.AddVertex (new OV.Coord3D (val.x, val.y, val.z)); + }); + } + + function GetLocalNormalIndex (obj, mesh, globalIndex) + { + return GetLocalIndex (obj.globalNormals, obj.currentMeshData.globalToCurrentNormals, globalIndex, function (val) { + return mesh.AddNormal (new OV.Coord3D (val.x, val.y, val.z)); + }); + } + + function GetLocalUVIndex (obj, mesh, globalIndex) + { + return GetLocalIndex (obj.globalUvs, obj.currentMeshData.globalToCurrentUvs, globalIndex, function (val) { + return mesh.AddTextureUV (new OV.Coord2D (val.x, val.y)); + }); + } + + let vertices = []; + let normals = []; + let uvs = []; + + for (let i = 0; i < parameters.length; i++) { + let vertexParams = parameters[i].split ('/'); + vertices.push (GetRelativeIndex (parseInt (vertexParams[0], 10), this.globalVertices.length)); + if (vertexParams.length > 1 && vertexParams[1].length > 0) { + uvs.push (GetRelativeIndex (parseInt (vertexParams[1], 10), this.globalUvs.length)); + } + if (vertexParams.length > 2 && vertexParams[2].length > 0) { + normals.push (GetRelativeIndex (parseInt (vertexParams[2], 10), this.globalNormals.length)); + } + } + + if (this.currentMesh === null) { + this.AddNewMesh (''); + } + + for (let i = 0; i < vertices.length - 2; i++) { + let v0 = GetLocalVertexIndex (this, this.currentMesh, vertices[0]); + let v1 = GetLocalVertexIndex (this, this.currentMesh, vertices[i + 1]); + let v2 = GetLocalVertexIndex (this, this.currentMesh, vertices[i + 2]); + if (v0 === null || v1 === null || v2 === null) { + this.SetError (); + this.SetMessage ('Invalid vertex index.'); + break; + } + let triangle = new OV.Triangle (v0, v1, v2); + if (normals.length === vertices.length) { + let n0 = GetLocalNormalIndex (this, this.currentMesh, normals[0]); + let n1 = GetLocalNormalIndex (this, this.currentMesh, normals[i + 1]); + let n2 = GetLocalNormalIndex (this, this.currentMesh, normals[i + 2]); + if (n0 === null || n1 === null || n2 === null) { + this.SetError (); + this.SetMessage ('Invalid normal index.'); + break; + } + triangle.SetNormals (n0, n1, n2); + } + if (uvs.length === vertices.length) { + let u0 = GetLocalUVIndex (this, this.currentMesh, uvs[0]); + let u1 = GetLocalUVIndex (this, this.currentMesh, uvs[i + 1]); + let u2 = GetLocalUVIndex (this, this.currentMesh, uvs[i + 2]); + if (u0 === null || u1 === null || u2 === null) { + this.SetError (); + this.SetMessage ('Invalid uv index.'); + break; + } + triangle.SetTextureUVs (u0, u1, u2); + } + if (this.currentMaterialIndex !== null) { + triangle.mat = this.currentMaterialIndex; + } + this.currentMesh.AddTriangle (triangle); + } + } +}; diff --git a/source/import/importeroff.js b/source/import/importeroff.js new file mode 100644 index 0000000..12f0319 --- /dev/null +++ b/source/import/importeroff.js @@ -0,0 +1,103 @@ +OV.ImporterOff = class extends OV.ImporterBase +{ + constructor () + { + super (); + this.model = null; + this.mesh = null; + this.status = null; + } + + ResetState () + { + this.mesh = new OV.Mesh (); + this.model.AddMesh (this.mesh); + this.status = { + vertexCount : 0, + faceCount : 0, + foundVertex : 0, + foundFace : 0 + }; + } + + CanImportExtension (extension) + { + return extension === 'off'; + } + + GetKnownFileFormats () + { + return { + 'off' : OV.FileFormat.Text + }; + } + + GetUpDirection () + { + return OV.Direction.Y; + } + + ImportContent (fileContent) + { + let obj = this; + OV.ReadLines (fileContent, function (line) { + if (!obj.IsError ()) { + obj.ProcessLine (line); + } + }); + } + + ProcessLine (line) + { + if (line[0] === '#') { + return; + } + + let parameters = OV.ParametersFromLine (line, '#'); + if (parameters.length === 0) { + return; + } + + if (parameters[0] === 'OFF') { + return; + } + + if (this.status.vertexCount === 0 && this.status.faceCount === 0) { + if (parameters.length > 1) { + this.status.vertexCount = parseInt (parameters[0], 10); + this.status.faceCount = parseInt (parameters[1], 10); + } + return; + } + + if (this.status.foundVertex < this.status.vertexCount) { + if (parameters.length >= 3) { + this.mesh.AddVertex (new OV.Coord3D ( + parseFloat (parameters[0]), + parseFloat (parameters[1]), + parseFloat (parameters[2]) + )); + this.status.foundVertex += 1; + } + return; + } + + if (this.status.foundFace < this.status.faceCount) { + if (parameters.length >= 4) { + let vertexCount = parseInt (parameters[0], 10); + if (parameters.length < vertexCount + 1) { + return; + } + for (let i = 0; i < vertexCount - 2; i++) { + let v0 = parseInt (parameters[1]); + let v1 = parseInt (parameters[i + 2]); + let v2 = parseInt (parameters[i + 3]); + let triangle = new OV.Triangle (v0, v1, v2); + this.mesh.AddTriangle (triangle); + } + this.status.foundFace += 1; + } + return; + } + } +}; diff --git a/source/import/importerply.js b/source/import/importerply.js new file mode 100644 index 0000000..763e6ae --- /dev/null +++ b/source/import/importerply.js @@ -0,0 +1,428 @@ +OV.PlyHeader = class +{ + constructor () + { + this.format = null; + this.elements = []; + } + + SetFormat (format) + { + this.format = format; + } + + AddElement (name, count) + { + this.elements.push ({ + name : name, + count : count, + format : [] + }); + } + + GetElements () + { + return this.elements; + } + + AddSingleFormat (elemType, name) + { + let lastElement = this.elements[this.elements.length - 1]; + lastElement.format.push ({ + name : name, + isSingle : true, + elemType : elemType + }); + } + + AddListFormat (countType, elemType, name) + { + let lastElement = this.elements[this.elements.length - 1]; + lastElement.format.push ({ + name : name, + isSingle : false, + countType : countType, + elemType : elemType + }); + } + + GetElement (name) + { + for (let i = 0; i < this.elements.length; i++) { + let element = this.elements[i]; + if (element.name === name) { + return element; + } + } + return null; + } + + Check () + { + let vertex = this.GetElement ('vertex'); + if (vertex === null || vertex.length === 0 || vertex.format.length < 3) { + return false; + } + + let face = this.GetElement ('face'); + if (this.format === 'ascii') { + if (face === null || face.count === 0 || face.format.length < 0) { + return false; + } + } else if (this.format === 'binary_little_endian' || this.format === 'binary_big_endian') { + let triStrips = this.GetElement ('tristrips'); + let hasFaces = (face !== null && face.count > 0 && face.format.length > 0); + let hasTriStrips = (triStrips !== null && triStrips.count > 0 && triStrips.format.length > 0); + if (!hasFaces && !hasTriStrips) { + return false; + } + } else { + return false; + } + + return true; + } +}; + +OV.ImporterPly = class extends OV.ImporterBase +{ + constructor () + { + super (); + this.model = null; + this.mesh = null; + } + + ResetState () + { + this.mesh = new OV.Mesh (); + this.model.AddMesh (this.mesh); + } + + CanImportExtension (extension) + { + return extension === 'ply'; + } + + GetKnownFileFormats () + { + return { + 'ply' : OV.FileFormat.Binary + }; + } + + GetUpDirection () + { + return OV.Direction.Y; + } + + ImportContent (fileContent) + { + let headerString = this.GetHeaderContent (fileContent); + let header = this.ReadHeader (headerString); + if (!header.Check ()) { + this.SetError (); + this.SetMessage ('Invalid header information.'); + return; + } + + if (header.format === 'ascii') { + let contentString = OV.ArrayBufferToUtf8String (fileContent); + contentString = contentString.substr (headerString.length); + this.ReadAsciiContent (header, contentString); + } else if (header.format === 'binary_little_endian' || header.format === 'binary_big_endian') { + this.ReadBinaryContent (header, fileContent, headerString.length); + } + } + + GetHeaderContent (fileContent) + { + let headerContent = ''; + let bufferView = new Uint8Array (fileContent); + let bufferIndex = 0; + for (bufferIndex = 0; bufferIndex < fileContent.byteLength; bufferIndex++) { + headerContent += String.fromCharCode (bufferView[bufferIndex]); + if (headerContent.endsWith ('end_header')) { + break; + } + } + bufferIndex += 1; + while (bufferIndex < fileContent.byteLength) { + let char = String.fromCharCode (bufferView[bufferIndex]); + headerContent += char; + bufferIndex += 1; + if (char === '\n') { + break; + } + } + return headerContent; + } + + ReadHeader (headerContent) + { + let header = new OV.PlyHeader (); + OV.ReadLines (headerContent, function (line) { + let parameters = OV.ParametersFromLine (line, null); + if (parameters.length === 0 || parameters[0] === 'comment') { + return; + } + + if (parameters[0] === 'ply') { + return; + } else if (parameters[0] === 'format' && parameters.length >= 2) { + header.SetFormat (parameters[1]); + } else if (parameters[0] === 'element' && parameters.length >= 3) { + header.AddElement (parameters[1], parseInt (parameters[2], 10)); + } else if (parameters[0] === 'property' && parameters.length >= 3) { + if (parameters[1] === 'list' && parameters.length >= 5) { + header.AddListFormat (parameters[2], parameters[3], parameters[4]); + } else { + header.AddSingleFormat (parameters[1], parameters[2]); + } + } + }); + + return header; + } + + ReadAsciiContent (header, fileContent) + { + let obj = this; + let vertex = header.GetElement ('vertex'); + let face = header.GetElement ('face'); + let foundVertex = 0; + let foundFace = 0; + OV.ReadLines (fileContent, function (line) { + if (obj.IsError ()) { + return; + } + + let parameters = OV.ParametersFromLine (line, null); + if (parameters.length === 0 || parameters[0] === 'comment') { + return; + } + + if (foundVertex < vertex.count) { + if (parameters.length >= 3) { + obj.mesh.AddVertex (new OV.Coord3D ( + parseFloat (parameters[0]), + parseFloat (parameters[1]), + parseFloat (parameters[2]) + )); + foundVertex += 1; + } + return; + } + + if (foundFace < face.count) { + if (parameters.length >= 4) { + let vertexCount = parseInt (parameters[0], 10); + if (parameters.length < vertexCount + 1) { + return; + } + for (let i = 0; i < vertexCount - 2; i++) { + let v0 = parseInt (parameters[1]); + let v1 = parseInt (parameters[i + 2]); + let v2 = parseInt (parameters[i + 3]); + let triangle = new OV.Triangle (v0, v1, v2); + obj.mesh.AddTriangle (triangle); + } + foundFace += 1; + } + return; + } + }); + } + + ReadBinaryContent (header, fileContent, headerLength) + { + function SkipAndGetColor (obj, reader, format, startIndex) + { + let r = null; + let g = null; + let b = null; + let a = 255; + + for (let i = startIndex; i < format.length; i++) { + let currFormat = format[i]; + let val = obj.ReadByFormat (reader, currFormat); + if (currFormat.name === 'red') { + r = val; + } else if (currFormat.name === 'green') { + g = val; + } else if (currFormat.name === 'blue') { + b = val; + } else if (currFormat.name === 'alpha') { + a = val; + } + } + + if (r !== null && g !== null && b !== null) { + return [r, g, b, a]; + } + + return null; + } + + function GetMaterialFromColor (obj, color, colorToMaterial) + { + function IntegerToHex (intVal) + { + let result = parseInt (intVal, 10).toString (16); + while (result.length < 2) { + result = '0' + result; + } + return result; + } + + if (color === null) { + return null; + } + + let materialName = '#' + IntegerToHex (color[0]) + IntegerToHex (color[1]) + IntegerToHex (color[2]) + IntegerToHex (color[3]); + let materialIndex = colorToMaterial[materialName]; + if (materialIndex === undefined) { + let material = new OV.Material (); + material.name = materialName; + material.diffuse = new OV.Color (color[0], color[1], color[2]); + material.opacity = color[3] / 255.0; + material.transparent = OV.IsLower (material.opacity, 1.0); + materialIndex = obj.model.AddMaterial (material); + colorToMaterial[materialName] = materialIndex; + } + + return materialIndex; + } + + function GetFaceColorByVertexColors (obj, v0, v1, v2, vertexColors, colorToMaterial) + { + let c0 = vertexColors[v0]; + let c1 = vertexColors[v1]; + let c2 = vertexColors[v2]; + let d0 = Math.abs (c0[0] - c1[0]) + Math.abs (c0[1] - c1[1]) + Math.abs (c0[2] - c1[2]); + let d1 = Math.abs (c1[0] - c2[0]) + Math.abs (c1[1] - c2[1]) + Math.abs (c1[2] - c2[2]); + let d2 = Math.abs (c0[0] - c2[0]) + Math.abs (c0[1] - c2[1]) + Math.abs (c0[2] - c2[2]); + let min = Math.min (d0, d1, d2); + let color = c0; + if (min === d1) { + color = c1; + } + return GetMaterialFromColor (obj, color, colorToMaterial); + } + + let reader = null; + if (header.format === 'binary_little_endian') { + reader = new OV.BinaryReader (fileContent, true); + } else if (header.format === 'binary_big_endian') { + reader = new OV.BinaryReader (fileContent, false); + } else { + return; + } + reader.Skip (headerLength); + + let vertexColors = []; + let colorToMaterial = {}; + let elements = header.GetElements (); + for (let elementIndex = 0; elementIndex < elements.length; elementIndex++) { + let element = elements[elementIndex]; + if (element.name === 'vertex') { + for (let vertexIndex = 0; vertexIndex < element.count; vertexIndex++) { + let x = this.ReadByFormat (reader, element.format[0]); + let y = this.ReadByFormat (reader, element.format[1]); + let z = this.ReadByFormat (reader, element.format[2]); + let color = SkipAndGetColor (this, reader, element.format, 3); + if (color !== null) { + vertexColors.push (color); + } + this.mesh.AddVertex (new OV.Coord3D (x, y, z)); + } + } else if (element.name === 'face') { + for (let faceIndex = 0; faceIndex < element.count; faceIndex++) { + let vertices = this.ReadByFormat (reader, element.format[0]); + let faceColor = SkipAndGetColor (this, reader, element.format, 1); + for (let i = 0; i < vertices.length - 2; i++) { + let v0 = vertices[0]; + let v1 = vertices[i + 1]; + let v2 = vertices[i + 2]; + let triangle = new OV.Triangle (v0, v1, v2); + if (faceColor !== null) { + triangle.mat = GetMaterialFromColor (this, faceColor, colorToMaterial); + } else if (vertexColors.length === this.model.VertexCount ()) { + triangle.mat = GetFaceColorByVertexColors (this, v0, v1, v2, vertexColors, colorToMaterial); + } + this.mesh.AddTriangle (triangle); + } + } + } else if (element.name === 'tristrips') { + for (let triStripIndex = 0; triStripIndex < element.count; triStripIndex++) { + let vertices = this.ReadByFormat (reader, element.format[0]); + this.SkipFormat (reader, element.format, 1); + let ccw = true; + for (let i = 0; i < vertices.length - 2; i++) { + let v0 = vertices[i]; + let v1 = vertices[i + 1]; + let v2 = vertices[i + 2]; + if (v2 === -1) { + i += 2; + ccw = true; + continue; + } + if (!ccw) { + let tmp = v1; + v1 = v2; + v2 = tmp; + } + ccw = !ccw; + let triangle = new OV.Triangle (v0, v1, v2); + this.mesh.AddTriangle (triangle); + } + } + } else { + this.SkipFormat (reader, element.format, 0); + } + } + } + + SkipFormat (reader, format, startIndex) + { + for (let i = startIndex; i < format.length; i++) { + this.ReadByFormat (reader, format[i]); + } + } + + ReadByFormat (reader, format) + { + function ReadType (reader, type) + { + if (type === 'char' || type === 'int8') { + return reader.ReadCharacter8 (); + } else if (type === 'uchar' || type === 'uint8') { + return reader.ReadUnsignedCharacter8 (); + } else if (type === 'short' || type === 'int16') { + return reader.ReadInteger16 (); + } else if (type === 'ushort' || type === 'uint16') { + return reader.ReadUnsignedInteger16 (); + } else if (type === 'int' || type === 'int32') { + return reader.ReadInteger32 (); + } else if (type === 'uint' || type === 'uint32') { + return reader.ReadUnsignedInteger32 (); + } else if (type === 'float' || type === 'float32') { + return reader.ReadFloat32 (); + } else if (type === 'double' || type === 'double64') { + return reader.ReadDouble64 (); + } + return null; + } + + if (format.isSingle) { + return ReadType (reader, format.elemType); + } else { + let list = []; + let count = ReadType (reader, format.countType); + for (let i = 0; i < count; i++) { + list.push (ReadType (reader, format.elemType)); + } + return list; + } + } +}; diff --git a/source/import/importerstl.js b/source/import/importerstl.js new file mode 100644 index 0000000..5886551 --- /dev/null +++ b/source/import/importerstl.js @@ -0,0 +1,168 @@ +OV.ImporterStl = class extends OV.ImporterBase +{ + constructor () + { + super (); + this.mesh = null; + this.triangle = null; + } + + ResetState () + { + this.mesh = new OV.Mesh (); + this.model.AddMesh (this.mesh); + this.triangle = null; + } + + CanImportExtension (extension) + { + return extension === 'stl'; + } + + GetKnownFileFormats () + { + return { + 'stl' : OV.FileFormat.Binary + }; + } + + GetUpDirection () + { + return OV.Direction.Z; + } + + ImportContent (fileContent) + { + if (this.IsBinaryStlFile (fileContent)) { + this.ProcessBinary (fileContent); + } else { + let obj = this; + let textContent = OV.ArrayBufferToUtf8String (fileContent); + OV.ReadLines (textContent, function (line) { + if (!obj.IsError ()) { + obj.ProcessLine (line); + } + }); + } + } + + IsBinaryStlFile (fileContent) + { + let byteLength = fileContent.byteLength; + if (byteLength < 84) { + return false; + } + + let reader = new OV.BinaryReader (fileContent, true); + reader.Skip (80); + + let triangleCount = reader.ReadUnsignedInteger32 (); + if (byteLength !== triangleCount * 50 + 84) { + return false; + } + + return true; + } + + ProcessLine (line) + { + if (line[0] === '#') { + return; + } + + let parameters = OV.ParametersFromLine (line, '#'); + if (parameters.length === 0) { + return; + } + + let keyword = parameters[0]; + if (keyword === 'solid') { + if (parameters.length > 1) { + let name = OV.NameFromLine (line, keyword.length, '#'); + this.mesh.SetName (name); + } + return; + } + + if (keyword === 'facet') { + this.triangle = new OV.Triangle (-1, -1, -1); + if (parameters.length >= 5 && parameters[1] === 'normal') { + let normalVector = new OV.Coord3D ( + parseFloat (parameters[2]), + parseFloat (parameters[3]), + parseFloat (parameters[4]) + ); + if (OV.IsPositive (normalVector.Length ())) { + let normalIndex = this.mesh.AddNormal (normalVector); + this.triangle.SetNormals ( + normalIndex, + normalIndex, + normalIndex + ); + } + } + return; + } + + if (keyword === 'vertex' && this.triangle !== null) { + if (parameters.length >= 4) { + let vertexIndex = this.mesh.AddVertex (new OV.Coord3D ( + parseFloat (parameters[1]), + parseFloat (parameters[2]), + parseFloat (parameters[3]) + )); + if (this.triangle.v0 === -1) { + this.triangle.v0 = vertexIndex; + } else if (this.triangle.v1 === -1) { + this.triangle.v1 = vertexIndex; + } else if (this.triangle.v2 === -1) { + this.triangle.v2 = vertexIndex; + } + } + return; + } + + if (keyword === 'endfacet' && this.triangle !== null) { + if (this.triangle.v0 !== -1 && this.triangle.v1 !== -1 && this.triangle.v2 !== null) { + this.mesh.AddTriangle (this.triangle); + } + this.triangle = null; + return; + } + } + + ProcessBinary (fileContent) + { + function ReadVector (reader) + { + let coord = new OV.Coord3D (); + coord.x = reader.ReadFloat32 (); + coord.y = reader.ReadFloat32 (); + coord.z = reader.ReadFloat32 (); + return coord; + } + + function AddVertex (mesh, reader) + { + let coord = ReadVector (reader); + return mesh.AddVertex (coord); + } + + let reader = new OV.BinaryReader (fileContent, true); + reader.Skip (80); + let triangleCount = reader.ReadUnsignedInteger32 (); + for (let i = 0; i < triangleCount; i++) { + let normalVector = ReadVector (reader); + let v0 = AddVertex (this.mesh, reader); + let v1 = AddVertex (this.mesh, reader); + let v2 = AddVertex (this.mesh, reader); + reader.Skip (2); + let triangle = new OV.Triangle (v0, v1, v2); + if (OV.IsPositive (normalVector.Length ())) { + let normal = this.mesh.AddNormal (normalVector); + triangle.SetNormals (normal, normal, normal); + } + this.mesh.AddTriangle (triangle); + } + } +}; diff --git a/source/import/importerutils.js b/source/import/importerutils.js new file mode 100644 index 0000000..6cbb1a3 --- /dev/null +++ b/source/import/importerutils.js @@ -0,0 +1,72 @@ +OV.NameFromLine = function (line, startIndex, commentChar) +{ + let name = line.substr (startIndex); + let commentStart = name.indexOf (commentChar); + if (commentStart !== -1) { + name = name.substr (0, commentStart); + } + return name.trim (); +}; + +OV.ParametersFromLine = function (line, commentChar) +{ + if (commentChar !== null) { + let commentStart = line.indexOf (commentChar); + if (commentStart !== -1) { + line = line.substr (0, commentStart).trim (); + } + } + return line.split (/\s+/u); +}; + +OV.ReadLines = function (str, onLine) +{ + function LineFound (line, onLine) + { + let trimmed = line.trim (); + if (trimmed.length > 0) { + onLine (trimmed); + } + } + + let cursor = 0; + let next = str.indexOf ('\n', cursor); + while (next !== -1) { + LineFound (str.substr (cursor, next - cursor), onLine); + cursor = next + 1; + next = str.indexOf ('\n', cursor); + } + LineFound (str.substr (cursor), onLine); +}; + +OV.IsPowerOfTwo = function (x) +{ + return (x & (x - 1)) === 0; +}; + +OV.NextPowerOfTwo = function (x) +{ + if (OV.IsPowerOfTwo (x)) { + return x; + } + let npot = Math.pow (2, Math.ceil (Math.log (x) / Math.log (2))); + return parseInt (npot, 10); +}; + +OV.ResizeImageToPowerOfTwoSides = function (image) +{ + if (OV.IsPowerOfTwo (image.width) && OV.IsPowerOfTwo (image.height)) { + return image; + } + + let width = OV.NextPowerOfTwo (image.width); + let height = OV.NextPowerOfTwo (image.height); + + let canvas = document.createElement ('canvas'); + canvas.width = width; + canvas.height = height; + + let context = canvas.getContext ('2d'); + context.drawImage (image, 0, 0, width, height); + return context.getImageData (0, 0, width, height); +}; diff --git a/source/io/binaryreader.js b/source/io/binaryreader.js new file mode 100644 index 0000000..94e7138 --- /dev/null +++ b/source/io/binaryreader.js @@ -0,0 +1,109 @@ +OV.BinaryReader = class +{ + constructor (arrayBuffer, isLittleEndian) + { + this.arrayBuffer = arrayBuffer; + this.dataView = new DataView (arrayBuffer); + this.isLittleEndian = isLittleEndian; + this.position = 0; + } + + GetPosition () + { + return this.position; + } + + SetPosition (position) + { + this.position = position; + } + + GetByteLength () + { + return this.arrayBuffer.byteLength; + } + + Skip (bytes) + { + this.position = this.position + bytes; + } + + End () + { + return this.position >= this.arrayBuffer.byteLength; + } + + ReadArrayBuffer (byteLength) + { + let originalBufferView = new Uint8Array (this.arrayBuffer); + let arrayBuffer = new ArrayBuffer (byteLength); + let bufferView = new Uint8Array (arrayBuffer); + let subArray = originalBufferView.subarray (this.position, this.position + byteLength); + bufferView.set (subArray, 0); + this.position += byteLength; + return arrayBuffer; + } + + ReadBoolean8 () + { + let result = this.dataView.getInt8 (this.position); + this.position = this.position + 1; + return result ? true : false; + } + + ReadCharacter8 () + { + let result = this.dataView.getInt8 (this.position); + this.position = this.position + 1; + return result; + } + + ReadUnsignedCharacter8 () + { + let result = this.dataView.getUint8 (this.position); + this.position = this.position + 1; + return result; + } + + ReadInteger16 () + { + let result = this.dataView.getInt16 (this.position, this.isLittleEndian); + this.position = this.position + 2; + return result; + } + + ReadUnsignedInteger16 () + { + let result = this.dataView.getUint16 (this.position, this.isLittleEndian); + this.position = this.position + 2; + return result; + } + + ReadInteger32 () + { + let result = this.dataView.getInt32 (this.position, this.isLittleEndian); + this.position = this.position + 4; + return result; + } + + ReadUnsignedInteger32 () + { + let result = this.dataView.getUint32 (this.position, this.isLittleEndian); + this.position = this.position + 4; + return result; + } + + ReadFloat32 () + { + let result = this.dataView.getFloat32 (this.position, this.isLittleEndian); + this.position = this.position + 4; + return result; + } + + ReadDouble64 () + { + let result = this.dataView.getFloat64 (this.position, this.isLittleEndian); + this.position = this.position + 8; + return result; + } +}; diff --git a/source/io/binarywriter.js b/source/io/binarywriter.js new file mode 100644 index 0000000..45db254 --- /dev/null +++ b/source/io/binarywriter.js @@ -0,0 +1,92 @@ +OV.BinaryWriter = class +{ + constructor (byteLength, isLittleEndian) + { + this.arrayBuffer = new ArrayBuffer (byteLength); + this.dataView = new DataView (this.arrayBuffer); + this.isLittleEndian = isLittleEndian; + this.position = 0; + } + + GetPosition () + { + return this.position; + } + + SetPosition (position) + { + this.position = position; + } + + End () + { + return this.position >= this.arrayBuffer.byteLength; + } + + GetBuffer () + { + return this.arrayBuffer; + } + + WriteArrayBuffer (arrayBuffer) + { + let bufferView = new Uint8Array (arrayBuffer); + let thisBufferView = new Uint8Array (this.arrayBuffer); + thisBufferView.set (bufferView, this.position); + this.position += arrayBuffer.byteLength; + } + + WriteBoolean8 (val) + { + this.dataView.setInt8 (this.position, val ? 1 : 0); + this.position = this.position + 1; + } + + WriteCharacter8 (val) + { + this.dataView.setInt8 (this.position, val); + this.position = this.position + 1; + } + + WriteUnsignedCharacter8 (val) + { + this.dataView.setUint8 (this.position, val); + this.position = this.position + 1; + } + + WriteInteger16 (val) + { + this.dataView.setInt16 (this.position, val, this.isLittleEndian); + this.position = this.position + 2; + } + + WriteUnsignedInteger16 (val) + { + this.dataView.setUint16 (this.position, val, this.isLittleEndian); + this.position = this.position + 2; + } + + WriteInteger32 (val) + { + this.dataView.setInt32 (this.position, val, this.isLittleEndian); + this.position = this.position + 4; + } + + WriteUnsignedInteger32 (val) + { + this.dataView.setUint32 (this.position, val, this.isLittleEndian); + this.position = this.position + 4; + } + + WriteFloat32 (val) + { + this.dataView.setFloat32 (this.position, val, this.isLittleEndian); + this.position = this.position + 4; + } + + WriteDouble64 (val) + { + this.dataView.setFloat64 (this.position, val, this.isLittleEndian); + this.position = this.position + 8; + } +}; diff --git a/source/io/bufferutils.js b/source/io/bufferutils.js new file mode 100644 index 0000000..17965e5 --- /dev/null +++ b/source/io/bufferutils.js @@ -0,0 +1,56 @@ +OV.ArrayBufferToUtf8String = function (buffer) +{ + let decoder = new TextDecoder ('utf-8'); + return decoder.decode (buffer); +}; + +OV.ArrayBufferToAsciiString = function (buffer) +{ + let text = ''; + let bufferView = new Uint8Array (buffer); + for (let i = 0; i < bufferView.byteLength; i++) { + text += String.fromCharCode (bufferView[i]); + } + return text; +}; + +OV.AsciiStringToArrayBuffer = function (str) +{ + let buffer = new ArrayBuffer (str.length); + let bufferView = new Uint8Array (buffer); + for (let i = 0; i < str.length; i++) { + bufferView[i] = str.charCodeAt (i); + } + return buffer; +}; + +OV.Base64DataURIToArrayBuffer = function (uri) +{ + let dataPrefix = 'data:'; + if (!uri.startsWith (dataPrefix)) { + return null; + } + + let mimeSeparator = uri.indexOf (';'); + if (mimeSeparator === -1) { + return null; + } + + let bufferSeparator = uri.indexOf (','); + if (bufferSeparator === -1) { + return null; + } + + let mimeType = uri.substr (dataPrefix.length, mimeSeparator - 5); + let base64String = atob (uri.substr (bufferSeparator + 1)); + let buffer = new ArrayBuffer (base64String.length); + let bufferView = new Uint8Array (buffer); + for (let i = 0; i < base64String.length; i++) { + bufferView[i] = base64String.charCodeAt (i); + } + + return { + mimeType : mimeType, + buffer : buffer + }; +}; diff --git a/source/io/fileutils.js b/source/io/fileutils.js new file mode 100644 index 0000000..3bc01fb --- /dev/null +++ b/source/io/fileutils.js @@ -0,0 +1,125 @@ +OV.FileSource = +{ + Url : 1, + File : 2 +}; + +OV.FileFormat = +{ + Text : 1, + Binary : 2 +}; + +OV.GetFileName = function (filePath) +{ + let firstSeparator = filePath.lastIndexOf ('/'); + if (firstSeparator === -1) { + firstSeparator = filePath.lastIndexOf ('\\'); + } + let fileName = filePath; + if (firstSeparator !== -1) { + fileName = filePath.substr (firstSeparator + 1); + } + return decodeURI (fileName); +}; + +OV.GetFileExtension = function (fileName) +{ + let firstPoint = fileName.lastIndexOf ('.'); + if (firstPoint === -1) { + return ''; + } + return fileName.substr (firstPoint + 1); +}; + +OV.RequestUrl = function (url, format, callbacks) +{ + function OnSuccess (result) + { + if (callbacks.success) { + callbacks.success (result); + } + if (callbacks.complete) { + callbacks.complete (); + } + } + + function OnError () + { + if (callbacks.error) { + callbacks.error (); + } + if (callbacks.complete) { + callbacks.complete (); + } + } + + let request = new XMLHttpRequest (); + request.open ('GET', url, true); + if (format === OV.FileFormat.Text) { + request.responseType = 'text'; + } else if (format === OV.FileFormat.Binary) { + request.responseType = 'arraybuffer'; + } else { + OnError (); + return; + } + + request.onload = function () { + if (request.status === 200) { + let response = request.response; + OnSuccess (response); + } else { + OnError (); + } + }; + + request.onerror = function () { + OnError (); + }; + + request.send (null); +}; + +OV.ReadFile = function (file, format, callbacks) +{ + function OnSuccess (result) + { + if (callbacks.success) { + callbacks.success (result); + } + if (callbacks.complete) { + callbacks.complete (); + } + } + + function OnError () + { + if (callbacks.error) { + callbacks.error (); + } + if (callbacks.complete) { + callbacks.complete (); + } + } + + let reader = new FileReader (); + + reader.onloadend = function (event) { + if (event.target.readyState === FileReader.DONE) { + OnSuccess (event.target.result); + } + }; + + reader.onerror = function () { + OnError (); + }; + + if (format === OV.FileFormat.Text) { + reader.readAsText (file); + } else if (format === OV.FileFormat.Binary) { + reader.readAsArrayBuffer (file); + } else { + OnError (); + } +}; diff --git a/source/io/textwriter.js b/source/io/textwriter.js new file mode 100644 index 0000000..7b92798 --- /dev/null +++ b/source/io/textwriter.js @@ -0,0 +1,41 @@ +OV.TextWriter = class +{ + constructor () + { + this.text = ''; + this.indentation = 0; + } + + GetText () + { + return this.text; + } + + Indent (diff) + { + this.indentation += diff; + } + + WriteArrayLine (arr) + { + this.WriteLine (arr.join (' ')); + } + + WriteLine (str) + { + this.WriteIndentation (); + this.Write (str + '\n'); + } + + WriteIndentation () + { + for (let i = 0; i < this.indentation; i++) { + this.Write (' '); + } + } + + Write (str) + { + this.text += str; + } +}; diff --git a/source/model/mesh.js b/source/model/mesh.js new file mode 100644 index 0000000..ab3927f --- /dev/null +++ b/source/model/mesh.js @@ -0,0 +1,100 @@ +OV.Mesh = class +{ + constructor () + { + this.name = ''; + this.vertices = []; + this.normals = []; + this.uvs = []; + this.triangles = []; + } + + GetName () + { + return this.name; + } + + SetName (name) + { + this.name = name; + } + + VertexCount () + { + return this.vertices.length; + } + + NormalCount () + { + return this.normals.length; + } + + TextureUVCount () + { + return this.uvs.length; + } + + TriangleCount () + { + return this.triangles.length; + } + + AddVertex (vertex) + { + this.vertices.push (vertex); + return this.vertices.length - 1; + } + + SetVertex (index, vertex) + { + this.vertices[index] = vertex; + } + + GetVertex (index) + { + return this.vertices[index]; + } + + AddNormal (normal) + { + this.normals.push (normal); + return this.normals.length - 1; + } + + SetNormal (index, normal) + { + this.normals[index] = normal; + } + + GetNormal (index) + { + return this.normals[index]; + } + + AddTextureUV (uv) + { + this.uvs.push (uv); + return this.uvs.length - 1; + } + + SetTextureUV (index, uv) + { + this.uvs[index] = uv; + } + + GetTextureUV (index) + { + return this.uvs[index]; + } + + AddTriangle (triangle) + { + this.triangles.push (triangle); + return this.triangles.length - 1; + } + + GetTriangle (index) + { + return this.triangles[index]; + } +}; diff --git a/source/model/meshbuffer.js b/source/model/meshbuffer.js new file mode 100644 index 0000000..df203a4 --- /dev/null +++ b/source/model/meshbuffer.js @@ -0,0 +1,144 @@ +OV.MeshPrimitiveBuffer = class +{ + constructor () + { + this.indices = []; + this.vertices = []; + this.normals = []; + this.uvs = []; + this.material = null; + } + + GetByteLength (indexTypeSize, numberTypeSize) + { + let indexCount = this.indices.length; + let numberCount = this.vertices.length + this.normals.length + this.uvs.length; + return indexCount * indexTypeSize + numberCount * numberTypeSize; + } +}; + +OV.MeshBuffer = class +{ + constructor () + { + this.primitives = []; + } + + GetByteLength (indexTypeSize, numberTypeSize) + { + let byteLength = 0; + for (let i = 0; i < this.primitives.length; i++) { + let primitive = this.primitives[i]; + byteLength += primitive.GetByteLength (indexTypeSize, numberTypeSize); + } + return byteLength; + } +}; + +OV.ConvertMeshToMeshBuffer = function (mesh) +{ + function AddVertexToPrimitiveBuffer (mesh, vertexIndex, normalIndex, uvIndex, primitiveBuffer, primitiveMaps) + { + function GetUVOrDefault (mesh, uvIndex, forceUVs) + { + if (uvIndex !== null) { + return mesh.GetTextureUV (uvIndex); + } else if (forceUVs) { + return new OV.Coord2D (0.0, 0.0); + } else { + return null; + } + } + + function AddVertex (mesh, vertexIndex, normalIndex, uvIndex, primitiveBuffer) + { + let forceUVs = mesh.TextureUVCount () > 0; + let vertex = mesh.GetVertex (vertexIndex); + let normal = mesh.GetNormal (normalIndex); + let primitiveVertexIndex = primitiveBuffer.vertices.length / 3; + primitiveBuffer.indices.push (primitiveVertexIndex); + primitiveBuffer.vertices.push (vertex.x, vertex.y, vertex.z); + primitiveBuffer.normals.push (normal.x, normal.y, normal.z); + let uv = GetUVOrDefault (mesh, uvIndex, forceUVs); + if (uv !== null) { + primitiveBuffer.uvs.push (uv.x, uv.y); + } + return { + index : primitiveVertexIndex, + normal : normal, + uv : uv + }; + } + + function FindMatchingPrimitiveVertex (mesh, primitiveVertices, normalIndex, uvIndex) + { + for (let i = 0; i < primitiveVertices.length; i++) { + let primitiveVertex = primitiveVertices[i]; + let normal = mesh.GetNormal (normalIndex); + let equalNormal = OV.CoordIsEqual3D (primitiveVertex.normal, normal); + let equalUv = false; + if (primitiveVertex.uv === null && uvIndex === null) { + equalUv = true; + } else { + let uv = GetUVOrDefault (mesh, uvIndex, true); + equalUv = OV.CoordIsEqual2D (primitiveVertex.uv, uv); + } + if (equalNormal && equalUv) { + return primitiveVertex; + } + } + return null; + } + + let primitiveVertices = primitiveMaps.meshToPrimitiveVertices[vertexIndex]; + if (primitiveVertices === undefined) { + let primitiveVertex = AddVertex (mesh, vertexIndex, normalIndex, uvIndex, primitiveBuffer); + primitiveMaps.meshToPrimitiveVertices[vertexIndex] = [primitiveVertex]; + } else { + let existingPrimitiveVertex = FindMatchingPrimitiveVertex (mesh, primitiveVertices, normalIndex, uvIndex); + if (existingPrimitiveVertex !== null) { + primitiveBuffer.indices.push (existingPrimitiveVertex.index); + } else { + let primitiveVertex = AddVertex (mesh, vertexIndex, normalIndex, uvIndex, primitiveBuffer); + primitiveVertices.push (primitiveVertex); + } + } + } + + let meshBuffer = new OV.MeshBuffer (); + + let triangleCount = mesh.TriangleCount (); + if (triangleCount === 0) { + return null; + } + + let triangleIndices = []; + for (let i = 0; i < triangleCount; i++) { + triangleIndices.push (i); + } + triangleIndices.sort (function (a, b) { + let aTriangle = mesh.GetTriangle (a); + let bTriangle = mesh.GetTriangle (b); + return aTriangle.mat - bTriangle.mat; + }); + + let primitiveBuffer = null; + let primitiveMaps = null; + for (let i = 0; i < triangleIndices.length; i++) { + let triangleIndex = triangleIndices[i]; + let triangle = mesh.GetTriangle (triangleIndex); + if (primitiveBuffer === null || primitiveBuffer.material !== triangle.mat) { + primitiveBuffer = new OV.MeshPrimitiveBuffer (); + primitiveBuffer.material = triangle.mat; + primitiveMaps = { + meshToPrimitiveVertices : {} + }; + meshBuffer.primitives.push (primitiveBuffer); + } + AddVertexToPrimitiveBuffer (mesh, triangle.v0, triangle.n0, triangle.u0, primitiveBuffer, primitiveMaps); + AddVertexToPrimitiveBuffer (mesh, triangle.v1, triangle.n1, triangle.u1, primitiveBuffer, primitiveMaps); + AddVertexToPrimitiveBuffer (mesh, triangle.v2, triangle.n2, triangle.u2, primitiveBuffer, primitiveMaps); + } + + return meshBuffer; +}; diff --git a/source/model/model.js b/source/model/model.js new file mode 100644 index 0000000..0f459c0 --- /dev/null +++ b/source/model/model.js @@ -0,0 +1,105 @@ +OV.Model = class +{ + constructor () + { + this.name = ''; + this.materials = []; + this.meshes = []; + } + + GetName () + { + return this.name; + } + + SetName (name) + { + this.name = name; + } + + MaterialCount () + { + return this.materials.length; + } + + MeshCount () + { + return this.meshes.length; + } + + VertexCount () + { + let count = 0; + for (let i = 0; i < this.meshes.length; i++) { + count += this.meshes[i].VertexCount (); + } + return count; + } + + NormalCount () + { + let count = 0; + for (let i = 0; i < this.meshes.length; i++) { + count += this.meshes[i].NormalCount (); + } + return count; + } + + TextureUVCount () + { + let count = 0; + for (let i = 0; i < this.meshes.length; i++) { + count += this.meshes[i].TextureUVCount (); + } + return count; + } + + TriangleCount () + { + let count = 0; + for (let i = 0; i < this.meshes.length; i++) { + count += this.meshes[i].TriangleCount (); + } + return count; + } + + AddMaterial (material) + { + this.materials.push (material); + return this.materials.length - 1; + } + + GetMaterial (index) + { + return this.materials[index]; + } + + AddMesh (mesh) + { + this.meshes.push (mesh); + return this.meshes.length - 1; + } + + AddMeshToIndex (mesh, index) + { + this.meshes.splice (index, 0, mesh); + return index; + } + + RemoveMesh (index) + { + this.meshes.splice (index, 1); + } + + GetMesh (index) + { + return this.meshes[index]; + } + + Clear () + { + this.name = ''; + this.materials = []; + this.meshes = []; + } +}; diff --git a/source/model/modelentities.js b/source/model/modelentities.js new file mode 100644 index 0000000..39bbe09 --- /dev/null +++ b/source/model/modelentities.js @@ -0,0 +1,229 @@ +OV.SRGBToLinear = function (component) +{ + if (component < 0.04045) { + return component * 0.0773993808; + } else { + return Math.pow (component * 0.9478672986 + 0.0521327014, 2.4); + } +}; + +OV.LinearToSRGB = function (component) +{ + if (component < 0.0031308) { + return component * 12.92; + } else { + return 1.055 * (Math.pow (component, 0.41666)) - 0.055; + } +}; + +OV.Color = class +{ + constructor (r, g, b) + { + this.r = r; // 0 .. 255 + this.g = g; // 0 .. 255 + this.b = b; // 0 .. 255 + } + + ToHexString () + { + function IntegerToHex (intVal) + { + let result = parseInt (intVal, 10).toString (16); + while (result.length < 2) { + result = '0' + result; + } + return result; + } + + let r = IntegerToHex (this.r); + let g = IntegerToHex (this.g); + let b = IntegerToHex (this.b); + return r + g + b; + } + + Clone () + { + return new OV.Color (this.r, this.g, this.b); + } +}; + +OV.TextureMap = class +{ + constructor () + { + this.name = null; + this.url = null; + this.offset = new OV.Coord2D (0.0, 0.0); + this.scale = new OV.Coord2D (1.0, 1.0); + this.rotation = 0.0; // radians + } + + HasTransformation () + { + if (!OV.CoordIsEqual2D (this.offset, new OV.Coord2D (0.0, 0.0))) { + return true; + } + if (!OV.CoordIsEqual2D (this.scale, new OV.Coord2D (1.0, 1.0))) { + return true; + } + if (!OV.IsEqual (this.rotation, 0.0)) { + return true; + } + return false; + } + + Clone () + { + let cloned = new OV.TextureMap (); + cloned.name = this.name; + cloned.url = this.url; + cloned.offset = this.offset.Clone (); + cloned.scale = this.scale.Clone (); + cloned.rotation = this.rotation; + return cloned; + } +}; + +OV.Material = class +{ + constructor () + { + this.name = ''; + + this.ambient = new OV.Color (0, 0, 0); + this.diffuse = new OV.Color (0, 0, 0); + this.specular = new OV.Color (0, 0, 0); + this.emissive = new OV.Color (0, 0, 0); + + this.shininess = 0.0; // 0.0 .. 1.0 + this.opacity = 1.0; // 0.0 .. 1.0 + + this.diffuseMap = null; + this.specularMap = null; + this.bumpMap = null; + this.normalMap = null; + this.emissiveMap = null; + + this.alphaTest = 0.0; // 0.0 .. 1.0 + this.transparent = false; + this.multiplyDiffuseMap = false; + } + + Clone () + { + let cloned = new OV.Material (); + + cloned.name = this.name; + + cloned.ambient = this.ambient.Clone (); + cloned.diffuse = this.diffuse.Clone (); + cloned.specular = this.specular.Clone (); + cloned.emissive = this.emissive.Clone (); + + cloned.shininess = this.shininess; + cloned.opacity = this.opacity; + + cloned.diffuseMap = this.CloneTextureMap (this.diffuseMap); + cloned.specularMap = this.CloneTextureMap (this.specularMap); + cloned.bumpMap = this.CloneTextureMap (this.bumpMap); + cloned.normalMap = this.CloneTextureMap (this.normalMap); + cloned.emissiveMap = this.CloneTextureMap (this.emissiveMap); + + cloned.alphaTest = this.alphaTest; + cloned.transparent = this.transparent; + cloned.multiplyDiffuseMap = this.multiplyDiffuseMap; + + return cloned; + } + + CloneTextureMap (textureMap) + { + if (textureMap === null) { + return null; + } + return textureMap.Clone (); + } +}; + +OV.Triangle = class +{ + constructor (v0, v1, v2) + { + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + + this.n0 = null; + this.n1 = null; + this.n2 = null; + + this.u0 = null; + this.u1 = null; + this.u2 = null; + + this.mat = null; + this.curve = null; + } + + HasVertices () + { + return this.v0 !== null && this.v1 !== null && this.v2 !== null; + } + + HasNormals () + { + return this.n0 !== null && this.n1 !== null && this.n2 !== null; + } + + HasTextureUVs () + { + return this.u0 !== null && this.u1 !== null && this.u2 !== null; + } + + SetVertices (v0, v1, v2) + { + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + return this; + } + + SetNormals (n0, n1, n2) + { + this.n0 = n0; + this.n1 = n1; + this.n2 = n2; + return this; + } + + SetTextureUVs (u0, u1, u2) + { + this.u0 = u0; + this.u1 = u1; + this.u2 = u2; + return this; + } + + SetMaterial (mat) + { + this.mat = mat; + return this; + } + + SetCurve (curve) + { + this.curve = curve; + return this; + } + + Clone () + { + let cloned = new OV.Triangle (this.v0, this.v1, this.v2); + cloned.SetNormals (this.n0, this.n1, this.n2); + cloned.SetTextureUVs (this.u0, this.u1, this.u2); + cloned.SetMaterial (this.mat); + cloned.SetCurve (this.curve); + return cloned; + } +}; diff --git a/source/model/modelfinalization.js b/source/model/modelfinalization.js new file mode 100644 index 0000000..52ee632 --- /dev/null +++ b/source/model/modelfinalization.js @@ -0,0 +1,241 @@ +OV.FinalizeModel = function (model, getDefaultMaterial) +{ + function FinalizeMesh (mesh, getDefaultMaterialIndex) + { + function CalculateCurveNormals (mesh) + { + function AddAverageNormal (mesh, triangle, vertexIndex, triangleNormals, vertexToTriangles) + { + let averageNormal = new OV.Coord3D (0.0, 0.0, 0.0); + let averageCount = 0; + + let neigTriangles = vertexToTriangles[vertexIndex]; + for (let i = 0; i < neigTriangles.length; i++) { + let neigIndex = neigTriangles[i]; + let neigTriangle = mesh.GetTriangle (neigIndex); + if (triangle.curve === neigTriangle.curve) { + averageNormal = OV.AddCoord3D (averageNormal, triangleNormals[neigIndex]); + averageCount = averageCount + 1; + } + } + + averageNormal.MultiplyScalar (1.0 / averageCount); + averageNormal.Normalize (); + return mesh.AddNormal (averageNormal); + } + + let triangleNormals = []; + let vertexToTriangles = {}; + + for (let vertexIndex = 0; vertexIndex < mesh.VertexCount (); vertexIndex++) { + vertexToTriangles[vertexIndex] = []; + } + + for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) { + let triangle = mesh.GetTriangle (triangleIndex); + let v0 = mesh.GetVertex (triangle.v0); + let v1 = mesh.GetVertex (triangle.v1); + let v2 = mesh.GetVertex (triangle.v2); + let normal = OV.CalculateTriangleNormal (v0, v1, v2); + triangleNormals.push (normal); + vertexToTriangles[triangle.v0].push (triangleIndex); + vertexToTriangles[triangle.v1].push (triangleIndex); + vertexToTriangles[triangle.v2].push (triangleIndex); + } + + for (let triangleIndex = 0; triangleIndex < mesh.TriangleCount (); triangleIndex++) { + let triangle = mesh.GetTriangle (triangleIndex); + if (triangle.n0 === null || triangle.n1 === null || triangle.n2 === null) { + let n0 = AddAverageNormal (mesh, triangle, triangle.v0, triangleNormals, vertexToTriangles); + let n1 = AddAverageNormal (mesh, triangle, triangle.v1, triangleNormals, vertexToTriangles); + let n2 = AddAverageNormal (mesh, triangle, triangle.v2, triangleNormals, vertexToTriangles); + triangle.SetNormals (n0, n1, n2); + } + } + } + + function FinalizeTriangle (mesh, triangle, status) + { + if (!triangle.HasNormals ()) { + if (triangle.curve === null || triangle.curve === 0) { + let v0 = mesh.GetVertex (triangle.v0); + let v1 = mesh.GetVertex (triangle.v1); + let v2 = mesh.GetVertex (triangle.v2); + let normal = OV.CalculateTriangleNormal (v0, v1, v2); + let normalIndex = mesh.AddNormal (normal); + triangle.SetNormals (normalIndex, normalIndex, normalIndex); + } else { + status.calculateCurveNormals = true; + } + } + if (triangle.mat === null) { + triangle.mat = status.getDefaultMaterialIndex (); + } + if (triangle.curve === null) { + triangle.curve = 0; + } + } + + let status = { + getDefaultMaterialIndex : getDefaultMaterialIndex, + calculateCurveNormals : false + }; + + for (let i = 0; i < mesh.TriangleCount (); i++) { + let triangle = mesh.GetTriangle (i); + FinalizeTriangle (mesh, triangle, status); + } + + if (status.calculateCurveNormals) { + CalculateCurveNormals (mesh); + } + } + + let defaultMaterialIndex = null; + let getDefaultMaterialIndex = function () { + if (defaultMaterialIndex === null) { + defaultMaterialIndex = model.AddMaterial (getDefaultMaterial ()); + } + return defaultMaterialIndex; + }; + + for (let i = 0; i < model.MeshCount (); i++) { + let mesh = model.GetMesh (i); + if (mesh.TriangleCount () === 0) { + model.RemoveMesh (i); + i = i - 1; + continue; + } + FinalizeMesh (mesh, getDefaultMaterialIndex); + } +}; + +OV.CheckModel = function (model) +{ + function IsCorrectValue (val) + { + if (val === undefined || val === null) { + return false; + } + return true; + } + + function IsCorrectNumber (val) + { + if (!IsCorrectValue (val)) { + return false; + } + if (isNaN (val)) { + return false; + } + return true; + } + + function IsCorrectIndex (val, count) + { + if (!IsCorrectNumber (val)) { + return false; + } + if (val < 0 || val >= count) { + return false; + } + return true; + } + + function CheckMesh (model, mesh) + { + function CheckTriangle (model, mesh, triangle) + { + if (!IsCorrectIndex (triangle.v0, mesh.VertexCount ())) { + return false; + } + if (!IsCorrectIndex (triangle.v1, mesh.VertexCount ())) { + return false; + } + if (!IsCorrectIndex (triangle.v2, mesh.VertexCount ())) { + return false; + } + if (!IsCorrectIndex (triangle.n0, mesh.NormalCount ())) { + return false; + } + if (!IsCorrectIndex (triangle.n1, mesh.NormalCount ())) { + return false; + } + if (!IsCorrectIndex (triangle.n2, mesh.NormalCount ())) { + return false; + } + if (triangle.HasTextureUVs ()) { + if (!IsCorrectIndex (triangle.u0, mesh.TextureUVCount ())) { + return false; + } + if (!IsCorrectIndex (triangle.u1, mesh.TextureUVCount ())) { + return false; + } + if (!IsCorrectIndex (triangle.u2, mesh.TextureUVCount ())) { + return false; + } + } + if (!IsCorrectIndex (triangle.mat, model.MaterialCount ())) { + return false; + } + if (!IsCorrectNumber (triangle.curve)) { + return false; + } + + return true; + } + + for (let i = 0; i < mesh.VertexCount (); i++) { + let vertex = mesh.GetVertex (i); + if (!IsCorrectNumber (vertex.x)) { + return false; + } + if (!IsCorrectNumber (vertex.y)) { + return false; + } + if (!IsCorrectNumber (vertex.z)) { + return false; + } + } + + for (let i = 0; i < mesh.NormalCount (); i++) { + let normal = mesh.GetNormal (i); + if (!IsCorrectNumber (normal.x)) { + return false; + } + if (!IsCorrectNumber (normal.y)) { + return false; + } + if (!IsCorrectNumber (normal.z)) { + return false; + } + } + + for (let i = 0; i < mesh.TextureUVCount (); i++) { + let uv = mesh.GetTextureUV (i); + if (!IsCorrectNumber (uv.x)) { + return false; + } + if (!IsCorrectNumber (uv.y)) { + return false; + } + } + + for (let i = 0; i < mesh.TriangleCount (); i++) { + let triangle = mesh.GetTriangle (i); + if (!CheckTriangle (model, mesh, triangle)) { + return false; + } + } + + return true; + } + + for (let i = 0; i < model.MeshCount (); i++) { + let mesh = model.GetMesh (i); + if (!CheckMesh (model, mesh)) { + return false; + } + } + return true; +}; diff --git a/source/model/modelutils.js b/source/model/modelutils.js new file mode 100644 index 0000000..e2f5df3 --- /dev/null +++ b/source/model/modelutils.js @@ -0,0 +1,163 @@ +OV.CalculateTriangleNormal = function (v0, v1, v2) +{ + let v = OV.SubCoord3D (v1, v0); + let w = OV.SubCoord3D (v2, v0); + + let normal = new OV.Coord3D ( + v.y * w.z - v.z * w.y, + v.z * w.x - v.x * w.z, + v.x * w.y - v.y * w.x + ); + + normal.Normalize (); + return normal; +}; + +OV.TransformMesh = function (mesh, transformation) +{ + if (transformation.IsIdentity ()) { + return; + } + + for (let i = 0; i < mesh.VertexCount (); i++) { + let vertex = mesh.GetVertex (i); + let transformed = transformation.TransformCoord3D (vertex); + vertex.x = transformed.x; + vertex.y = transformed.y; + vertex.z = transformed.z; + } + + if (mesh.NormalCount () > 0) { + let trs = transformation.GetMatrix ().DecomposeTRS (); + let normalMatrix = new OV.Matrix ().ComposeTRS ([0.0, 0.0, 0.0], trs.rotation, [1.0, 1.0, 1.0]); + let normalTransformation = new OV.Transformation (normalMatrix); + for (let i = 0; i < mesh.NormalCount (); i++) { + let normal = mesh.GetNormal (i); + let transformed = normalTransformation.TransformCoord3D (normal); + normal.x = transformed.x; + normal.y = transformed.y; + normal.z = transformed.z; + } + } +}; + +OV.GetMeshBoundingBox = function (mesh) +{ + let min = new OV.Coord3D (Infinity, Infinity, Infinity); + let max = new OV.Coord3D (-Infinity, -Infinity, -Infinity); + + for (let i = 0; i < mesh.VertexCount (); i++) { + let vertex = mesh.GetVertex (i); + min.x = Math.min (min.x, vertex.x); + min.y = Math.min (min.y, vertex.y); + min.z = Math.min (min.z, vertex.z); + max.x = Math.max (max.x, vertex.x); + max.y = Math.max (max.y, vertex.y); + max.z = Math.max (max.z, vertex.z); + } + + return [min, max]; +}; + +OV.IsModelEmpty = function (model) +{ + for (let i = 0; i < model.MeshCount (); i++) { + let mesh = model.GetMesh (i); + if (mesh.TriangleCount () > 0) { + return false; + } + } + return true; +}; + +OV.CloneMesh = function (mesh) +{ + let cloned = new OV.Mesh (); + + cloned.SetName (mesh.GetName ()); + + for (let i = 0; i < mesh.VertexCount (); i++) { + let vertex = mesh.GetVertex (i); + cloned.AddVertex (vertex.Clone ()); + } + + for (let i = 0; i < mesh.NormalCount (); i++) { + let normal = mesh.GetNormal (i); + cloned.AddNormal (normal.Clone ()); + } + + for (let i = 0; i < mesh.TextureUVCount (); i++) { + let uv = mesh.GetTextureUV (i); + cloned.AddTextureUV (uv.Clone ()); + } + + for (let i = 0; i < mesh.TriangleCount (); i++) { + let triangle = mesh.GetTriangle (i); + cloned.AddTriangle (triangle.Clone ()); + } + + return cloned; +}; + +OV.CreateMergedModel = function (model) +{ + function MergeMesh (mesh, mergedMesh) + { + let vertexOffset = mergedMesh.VertexCount (); + let normalOffset = mergedMesh.NormalCount (); + let uvOffset = mergedMesh.TextureUVCount (); + + for (let i = 0; i < mesh.VertexCount (); i++) { + let vertex = mesh.GetVertex (i); + mergedMesh.AddVertex (vertex.Clone ()); + } + for (let i = 0; i < mesh.NormalCount (); i++) { + let normal = mesh.GetNormal (i); + mergedMesh.AddNormal (normal.Clone ()); + } + for (let i = 0; i < mesh.TextureUVCount (); i++) { + let uv = mesh.GetTextureUV (i); + mergedMesh.AddTextureUV (uv.Clone ()); + } + + for (let i = 0; i < mesh.TriangleCount (); i++) { + let triangle = mesh.GetTriangle (i); + let newTriangle = triangle.Clone (); + newTriangle.SetVertices ( + triangle.v0 + vertexOffset, + triangle.v1 + vertexOffset, + triangle.v2 + vertexOffset + ); + newTriangle.SetNormals ( + triangle.n0 + normalOffset, + triangle.n1 + normalOffset, + triangle.n2 + normalOffset + ); + if (newTriangle.HasTextureUVs ()) { + newTriangle.SetTextureUVs ( + triangle.u0 + uvOffset, + triangle.u1 + uvOffset, + triangle.u2 + uvOffset + ); + } + mergedMesh.AddTriangle (newTriangle); + } + } + + let mergedModel = new OV.Model (); + mergedModel.SetName (model.GetName ()); + + for (let i = 0; i < model.MaterialCount (); i++) { + let material = model.GetMaterial (i); + mergedModel.AddMaterial (material.Clone ()); + } + + let mergedMesh = new OV.Mesh (); + for (let i = 0; i < model.MeshCount (); i++) { + let mesh = model.GetMesh (i); + MergeMesh (mesh, mergedMesh); + } + + mergedModel.AddMesh (mergedMesh); + return mergedModel; +}; diff --git a/source/viewer/domutils.js b/source/viewer/domutils.js new file mode 100644 index 0000000..bee2004 --- /dev/null +++ b/source/viewer/domutils.js @@ -0,0 +1,21 @@ +OV.GetInnerDimensions = function (element, outerWidth, outerHeight) +{ + function GetInt (parameter) + { + return Math.round (parseFloat (parameter)); + } + + let style = getComputedStyle (element); + let width = outerWidth - + GetInt (style.borderLeftWidth) - GetInt (style.borderRightWidth) - + GetInt (style.marginLeft) - GetInt (style.marginRight) - + GetInt (style.paddingLeft) - GetInt (style.paddingRight); + let height = outerHeight - + GetInt (style.borderTopWidth) - GetInt (style.borderBottomWidth) - + GetInt (style.marginTop) - GetInt (style.marginBottom) - + GetInt (style.paddingTop) - GetInt (style.paddingBottom); + return { + width : width, + height : height + }; +}; diff --git a/source/viewer/domviewer.js b/source/viewer/domviewer.js new file mode 100644 index 0000000..b606cb4 --- /dev/null +++ b/source/viewer/domviewer.js @@ -0,0 +1,83 @@ +OV.Init3DViewerElements = function () +{ + function LoadElement (element) + { + let canvas = document.createElement ('canvas'); + element.appendChild (canvas); + + let viewer = new OV.Viewer (); + viewer.Init (canvas); + + let width = parseInt (element.getAttribute ('width')); + let height = parseInt (element.getAttribute ('height')); + element.style.width = width + 'px'; + element.style.height = height + 'px'; + viewer.Resize (width, height); + + let loader = new OV.ThreeModelLoader (); + let progressDiv = null; + loader.Init ({ + onLoadStart : function () { + canvas.style.display = 'none'; + progressDiv = document.createElement ('div'); + element.appendChild (progressDiv); + progressDiv.innerHTML = 'Loading model...'; + }, + onFilesLoaded : function () { + progressDiv.innerHTML = 'Importing model...'; + }, + onModelImported : function () { + progressDiv.innerHTML = 'Visualizing model...'; + }, + onModelFinished : function (importResult, threeMeshes) { + element.removeChild (progressDiv); + canvas.style.display = 'initial'; + viewer.AddMeshes (threeMeshes); + let boundingSphere = viewer.GetBoundingSphere (function (meshUserData) { + return true; + }); + viewer.AdjustClippingPlanes (boundingSphere); + viewer.FitToWindow (boundingSphere, false); + }, + onTextureLoaded : function () { + viewer.Render (); + }, + onLoadError : function (importerError) { + progressDiv.innerHTML = 'Unknown error.'; + }, + }); + + let model = element.getAttribute ('model'); + if (!model) { + return; + } + + let modelUrls = model.split (','); + if (modelUrls.length === 0) { + return; + } + + let camera = element.getAttribute ('camera'); + if (camera) { + let cameraParams = camera.split (','); + if (cameraParams.length === 9) { + let camera = new OV.Camera ( + new OV.Coord3D (parseFloat (cameraParams[0]), parseFloat (cameraParams[1]), parseFloat (cameraParams[2])), + new OV.Coord3D (parseFloat (cameraParams[3]), parseFloat (cameraParams[4]), parseFloat (cameraParams[5])), + new OV.Coord3D (parseFloat (cameraParams[6]), parseFloat (cameraParams[7]), parseFloat (cameraParams[8])) + ); + viewer.SetCamera (camera); + } + } + + loader.LoadFromUrlList (modelUrls); + } + + window.addEventListener ('load', function () { + let elements = document.getElementsByClassName ('online_3d_viewer'); + for (let i = 0; i < elements.length; i++) { + let element = elements[i]; + LoadElement (element); + } + }); +}; diff --git a/source/viewer/navigation.js b/source/viewer/navigation.js new file mode 100644 index 0000000..4945612 --- /dev/null +++ b/source/viewer/navigation.js @@ -0,0 +1,549 @@ +OV.GetClientCoordinates = function (canvas, clientX, clientY) +{ + if (canvas.getBoundingClientRect) { + let clientRect = canvas.getBoundingClientRect (); + clientX -= clientRect.left; + clientY -= clientRect.top; + } + if (window.pageXOffset && window.pageYOffset) { + clientX += window.pageXOffset; + clientY += window.pageYOffset; + } + return (new OV.Coord2D (clientX, clientY)); +}; + +OV.Camera = class +{ + constructor (eye, center, up) + { + this.eye = eye; + this.center = center; + this.up = up; + } + + Clone () + { + return new OV.Camera ( + this.eye.Clone (), + this.center.Clone (), + this.up.Clone () + ); + } +}; + +OV.MouseInteraction = class +{ + constructor () + { + this.prev = new OV.Coord2D (0.0, 0.0); + this.curr = new OV.Coord2D (0.0, 0,0); + this.diff = new OV.Coord2D (0.0, 0.0); + this.buttons = []; + } + + Down (canvas, ev) + { + this.buttons.push (ev.which); + this.curr = this.GetCurrent (canvas, ev); + this.prev = this.curr.Clone (); + } + + Move (canvas, ev) + { + this.curr = this.GetCurrent (canvas, ev); + this.diff = OV.SubCoord2D (this.curr, this.prev); + this.prev = this.curr.Clone (); + } + + Up (canvas, ev) + { + let buttonIndex = this.buttons.indexOf (ev.which); + if (buttonIndex !== -1) { + this.buttons.splice (buttonIndex, 1); + } + this.curr = this.GetCurrent (canvas, ev); + } + + Leave (canvas, ev) + { + this.buttons = []; + this.curr = this.GetCurrent (canvas, ev); + } + + IsButtonDown () + { + return this.buttons.length > 0; + } + + GetButton () + { + let length = this.buttons.length; + if (length === 0) { + return 0; + } + return this.buttons[length - 1]; + } + + GetMoveDiff () + { + return this.diff; + } + + GetCurrent (canvas, ev) + { + return OV.GetClientCoordinates (canvas, ev.clientX, ev.clientY); + } +}; + +OV.TouchInteraction = class +{ + constructor () + { + this.prevPos = new OV.Coord2D (0.0, 0.0); + this.currPos = new OV.Coord2D (0.0, 0,0); + this.diffPos = new OV.Coord2D (0.0, 0.0); + this.prevDist = 0.0; + this.currDist = 0.0; + this.diffDist = 0.0; + this.fingers = 0; + } + + Start (canvas, ev) + { + if (ev.touches.length === 0) { + return; + } + + this.fingers = ev.touches.length; + + this.currPos = this.GetCurrent (canvas, ev); + this.prevPos = this.currPos.Clone (); + + this.currDist = this.GetTouchDistance (canvas, ev); + this.prevDist = this.currDist; + } + + Move (canvas, ev) + { + if (ev.touches.length === 0) { + return; + } + this.currPos = this.GetCurrent (canvas, ev); + this.diffPos = OV.SubCoord2D (this.currPos, this.prevPos); + this.prevPos = this.currPos.Clone (); + + this.currDist = this.GetTouchDistance (canvas, ev); + this.diffDist = this.currDist - this.prevDist; + this.prevDist = this.currDist; + } + + End (canvas, ev) + { + if (ev.touches.length === 0) { + return; + } + this.fingers = 0; + this.currPos = this.GetCurrent (canvas, ev); + this.currDist = this.GetTouchDistance (canvas, ev); + } + + IsFingerDown () + { + return this.fingers !== 0; + } + + GetFingerCount () + { + return this.fingers; + } + + GetMoveDiff () + { + return this.diffPos; + } + + GetDistanceDiff () + { + return this.diffDist; + } + + GetCurrent (canvas, ev) + { + let coord = null; + if (ev.touches.length !== 0) { + let touchEv = ev.touches[0]; + coord = OV.GetClientCoordinates (canvas, touchEv.pageX, touchEv.pageY); + } + return coord; + } + + GetTouchDistance (canvas, ev) + { + if (ev.touches.length !== 2) { + return 0.0; + } + let touchEv1 = ev.touches[0]; + let touchEv2 = ev.touches[1]; + let distance = OV.CoordDistance2D ( + OV.GetClientCoordinates (canvas, touchEv1.pageX, touchEv1.pageY), + OV.GetClientCoordinates (canvas, touchEv2.pageX, touchEv2.pageY) + ); + return distance; + } +}; + +OV.ClickDetector = class +{ + constructor () + { + this.isClick = false; + this.button = 0; + } + + Down (ev) + { + this.isClick = true; + this.button = ev.which; + } + + Move () + { + this.isClick = false; + } + + Up (ev) + { + if (this.isClick && ev.which !== this.button) { + this.isClick = false; + } + } + + Leave (ev) + { + this.isClick = false; + this.button = 0; + } + + IsClick () + { + return this.isClick; + } +}; + +OV.Navigation = class +{ + constructor (canvas, camera) + { + this.canvas = canvas; + this.camera = camera; + this.orbitCenter = this.camera.center.Clone (); + this.fixUpVector = true; + + this.mouse = new OV.MouseInteraction (); + this.touch = new OV.TouchInteraction (); + this.clickDetector = new OV.ClickDetector (); + + this.onUpdate = null; + this.onClick = null; + + if (this.canvas.addEventListener) { + this.canvas.addEventListener ('mousedown', this.OnMouseDown.bind (this)); + this.canvas.addEventListener ('DOMMouseScroll', this.OnMouseWheel.bind (this)); + this.canvas.addEventListener ('mousewheel', this.OnMouseWheel.bind (this)); + this.canvas.addEventListener ('touchstart', this.OnTouchStart.bind (this)); + this.canvas.addEventListener ('touchmove', this.OnTouchMove.bind (this)); + this.canvas.addEventListener ('touchend', this.OnTouchEnd.bind (this)); + this.canvas.addEventListener ('contextmenu', this.OnContextMenu.bind (this)); + } + if (document.addEventListener) { + document.addEventListener ('mousemove', this.OnMouseMove.bind (this)); + document.addEventListener ('mouseup', this.OnMouseUp.bind (this)); + document.addEventListener ('mouseleave', this.OnMouseLeave.bind (this)); + } + } + + SetUpdateHandler (onUpdate) + { + this.onUpdate = onUpdate; + } + + SetClickHandler (onClick) + { + this.onClick = onClick; + } + + IsFixUpVector () + { + return this.fixUpVector; + } + + SetFixUpVector (fixUpVector) + { + this.fixUpVector = fixUpVector; + } + + GetCamera () + { + return this.camera; + } + + SetCamera (camera) + { + this.camera = camera; + } + + MoveCamera (newCamera, stepCount) + { + function Step (obj, steps, count, index) + { + obj.camera.eye = steps.eye[index]; + obj.camera.center = steps.center[index]; + obj.camera.up = steps.up[index]; + obj.Update (); + + if (index < count - 1) { + requestAnimationFrame (function () { + Step (obj, steps, count, index + 1); + }); + } + } + + if (stepCount === 0) { + this.SetCamera (newCamera); + return; + } + + if (OV.CoordIsEqual3D (this.camera.eye, newCamera.eye) && + OV.CoordIsEqual3D (this.camera.center, newCamera.center) && + OV.CoordIsEqual3D (this.camera.up, newCamera.up)) + { + return; + } + + let tweenFunc = OV.ParabolicTweenFunction; + let obj = this; + let steps = { + eye : OV.TweenCoord3D (this.camera.eye, newCamera.eye, stepCount, tweenFunc), + center : OV.TweenCoord3D (this.camera.center, newCamera.center, stepCount, tweenFunc), + up : OV.TweenCoord3D (this.camera.up, newCamera.up, stepCount, tweenFunc) + }; + requestAnimationFrame (function () { + Step (obj, steps, stepCount, 0); + }); + this.Update (); + } + + FitToSphere (center, radius, fov) + { + if (OV.IsZero (radius)) { + return; + } + + let fitCamera = this.GetFitToSphereCamera (center, radius, fov); + this.camera = fitCamera; + + this.orbitCenter = this.camera.center.Clone (); + this.Update (); + } + + GetFitToSphereCamera (center, radius, fov) + { + if (OV.IsZero (radius)) { + return null; + } + + let fitCamera = this.camera.Clone (); + + let offsetToOrigo = OV.SubCoord3D (fitCamera.center, center); + fitCamera.eye = OV.SubCoord3D (fitCamera.eye, offsetToOrigo); + fitCamera.center = center.Clone (); + + let centerEyeDirection = OV.SubCoord3D (fitCamera.eye, fitCamera.center).Normalize (); + let fieldOfView = fov / 2.0; + if (this.canvas.width < this.canvas.height) { + fieldOfView = fieldOfView * this.canvas.width / this.canvas.height; + } + let distance = radius / Math.sin (fieldOfView * OV.DegRad); + + fitCamera.eye = fitCamera.center.Clone ().Offset (centerEyeDirection, distance); + this.orbitCenter = fitCamera.center.Clone (); + return fitCamera; + } + + OnMouseDown (ev) + { + ev.preventDefault (); + this.mouse.Down (this.canvas, ev); + this.clickDetector.Down (ev); + } + + OnMouseMove (ev) + { + this.mouse.Move (this.canvas, ev); + this.clickDetector.Move (); + if (!this.mouse.IsButtonDown ()) { + return; + } + + let moveDiff = this.mouse.GetMoveDiff (); + let mouseButton = this.mouse.GetButton (); + if (mouseButton === 1) { + let orbitRatio = 0.5; + this.Orbit (moveDiff.x * orbitRatio, moveDiff.y * orbitRatio); + } else if (mouseButton === 3) { + let eyeCenterDistance = OV.CoordDistance3D (this.camera.eye, this.camera.center); + let panRatio = 0.001 * eyeCenterDistance; + this.Pan (moveDiff.x * panRatio, moveDiff.y * panRatio); + } + + this.Update (); + } + + OnMouseUp (ev) + { + this.mouse.Up (this.canvas, ev); + this.clickDetector.Up (ev); + if (this.clickDetector.IsClick ()) { + let isCtrlPressed = (ev.ctrlKey || ev.metaKey); + this.Click (ev.which, isCtrlPressed, ev.clientX, ev.clientY); + } + } + + OnMouseLeave (ev) + { + this.mouse.Leave (this.canvas, ev); + this.clickDetector.Leave (ev); + } + + OnTouchStart (ev) + { + ev.preventDefault (); + this.touch.Start (this.canvas, ev); + } + + OnTouchMove (ev) + { + ev.preventDefault (); + this.touch.Move (this.canvas, ev); + if (!this.touch.IsFingerDown ()) { + return; + } + + let moveDiff = this.touch.GetMoveDiff (); + let distanceDiff = this.touch.GetDistanceDiff (); + let fingerCount = this.touch.GetFingerCount (); + if (fingerCount === 1) { + let orbitRatio = 0.5; + this.Orbit (moveDiff.x * orbitRatio, moveDiff.y * orbitRatio); + } else if (fingerCount === 2) { + let zoomRatio = 0.005; + this.Zoom (distanceDiff * zoomRatio); + let panRatio = 0.001 * OV.CoordDistance3D (this.camera.eye, this.camera.center); + this.Pan (moveDiff.x * panRatio, moveDiff.y * panRatio); + } + + this.Update (); + } + + OnTouchEnd (ev) + { + ev.preventDefault (); + this.touch.End (this.canvas, ev); + } + + OnMouseWheel (ev) + { + ev.preventDefault (); + let params = ev || window.event; + + let delta = 0; + if (params.detail) { + delta = -params.detail; + } else if (params.wheelDelta) { + delta = params.wheelDelta / 40; + } + + let ratio = 0.1; + if (delta < 0) { + ratio = ratio * -1.0; + } + + this.Zoom (ratio); + this.Update (); + } + + OnContextMenu (ev) + { + ev.preventDefault (); + } + + Orbit (angleX, angleY) + { + let radAngleX = angleX * OV.DegRad; + let radAngleY = angleY * OV.DegRad; + + let viewDirection = OV.SubCoord3D (this.camera.center, this.camera.eye).Normalize (); + let horizontalDirection = OV.CrossVector3D (viewDirection, this.camera.up).Normalize (); + let differentCenter = !OV.CoordIsEqual3D (this.orbitCenter, this.camera.center); + + if (this.fixUpVector) { + let originalAngle = OV.VectorAngle3D (viewDirection, this.camera.up); + let newAngle = originalAngle + radAngleY; + if (OV.IsGreater (newAngle, 0.0) && OV.IsLower (newAngle, Math.PI)) { + this.camera.eye.Rotate (horizontalDirection, -radAngleY, this.orbitCenter); + if (differentCenter) { + this.camera.center.Rotate (horizontalDirection, -radAngleY, this.orbitCenter); + } + } + this.camera.eye.Rotate (this.camera.up, -radAngleX, this.orbitCenter); + if (differentCenter) { + this.camera.center.Rotate (this.camera.up, -radAngleX, this.orbitCenter); + } + } else { + let verticalDirection = OV.CrossVector3D (horizontalDirection, viewDirection).Normalize (); + this.camera.eye.Rotate (horizontalDirection, -radAngleY, this.orbitCenter); + this.camera.eye.Rotate (verticalDirection, -radAngleX, this.orbitCenter); + if (differentCenter) { + this.camera.center.Rotate (horizontalDirection, -radAngleY, this.orbitCenter); + this.camera.center.Rotate (verticalDirection, -radAngleX, this.orbitCenter); + } + this.camera.up = verticalDirection; + } + } + + Pan (moveX, moveY) + { + let viewDirection = OV.SubCoord3D (this.camera.center, this.camera.eye).Normalize (); + let horizontalDirection = OV.CrossVector3D (viewDirection, this.camera.up).Normalize (); + let verticalDirection = OV.CrossVector3D (horizontalDirection, viewDirection).Normalize (); + + this.camera.eye.Offset (horizontalDirection, -moveX); + this.camera.center.Offset (horizontalDirection, -moveX); + + this.camera.eye.Offset (verticalDirection, moveY); + this.camera.center.Offset (verticalDirection, moveY); + } + + Zoom (ratio) + { + let direction = OV.SubCoord3D (this.camera.center, this.camera.eye); + let distance = direction.Length (); + let move = distance * ratio; + this.camera.eye.Offset (direction, move); + } + + Update () + { + if (this.onUpdate) { + this.onUpdate (); + } + } + + Click (button, isCtrlPressed, clientX, clientY) + { + if (this.onClick) { + let mouseCoords = OV.GetClientCoordinates (this.canvas, clientX, clientY); + this.onClick (button, isCtrlPressed, mouseCoords); + } + } +}; diff --git a/source/viewer/viewer.js b/source/viewer/viewer.js new file mode 100644 index 0000000..0a8a8fe --- /dev/null +++ b/source/viewer/viewer.js @@ -0,0 +1,375 @@ +OV.GetDefaultCamera = function (direction) +{ + if (direction === OV.Direction.X) { + return new OV.Camera ( + new OV.Coord3D (2.0, -3.0, 1.5), + new OV.Coord3D (0.0, 0.0, 0.0), + new OV.Coord3D (1.0, 0.0, 0.0) + ); + } else if (direction === OV.Direction.Y) { + return new OV.Camera ( + new OV.Coord3D (-1.5, 2.0, 3.0), + new OV.Coord3D (0.0, 0.0, 0.0), + new OV.Coord3D (0.0, 1.0, 0.0) + ); + } else if (direction === OV.Direction.Z) { + return new OV.Camera ( + new OV.Coord3D (-1.5, -3.0, 2.0), + new OV.Coord3D (0.0, 0.0, 0.0), + new OV.Coord3D (0.0, 0.0, 1.0) + ); + } + return null; +}; + +OV.UpVector = class +{ + constructor () + { + this.direction = OV.Direction.Z; + this.isFixed = true; + this.isFlipped = false; + } + + SetDirection (newDirection, oldCamera) + { + this.direction = newDirection; + this.isFlipped = false; + + let defaultCamera = OV.GetDefaultCamera (this.direction); + let defaultDir = OV.SubCoord3D (defaultCamera.eye, defaultCamera.center); + + let distance = OV.CoordDistance3D (oldCamera.center, oldCamera.eye); + let newEye = oldCamera.center.Clone ().Offset (defaultDir, distance); + + let newCamera = oldCamera.Clone (); + if (this.direction === OV.Direction.Y) { + newCamera.up = new OV.Coord3D (0.0, 1.0, 0.0); + newCamera.eye = newEye; + } else if (this.direction === OV.Direction.Z) { + newCamera.up = new OV.Coord3D (0.0, 0.0, 1.0); + newCamera.eye = newEye; + } + return newCamera; + } + + SetFixed (isFixed, oldCamera) + { + this.isFixed = isFixed; + if (this.isFixed) { + return this.SetDirection (this.direction, oldCamera); + } + return null; + } + + Flip (oldCamera) + { + this.isFlipped = !this.isFlipped; + let newCamera = oldCamera.Clone (); + newCamera.up.MultiplyScalar (-1.0); + return newCamera; + } +}; + +OV.Viewer = class +{ + constructor () + { + this.canvas = null; + this.renderer = null; + this.scene = null; + this.camera = null; + this.light = null; + this.navigation = null; + this.upVector = null; + this.settings = { + animationSteps : 40 + }; + } + + Init (canvas) + { + this.canvas = canvas; + this.canvas.id = 'viewer'; + this.canvas.addEventListener ('contenxtmenu', function (ev) { + ev.preventDefault (); + }); + + let parameters = { + canvas : this.canvas, + antialias : true + }; + + this.renderer = new THREE.WebGLRenderer (parameters); + this.renderer.setClearColor ('#ffffff', 1); + this.renderer.setSize (this.canvas.width, this.canvas.height); + + this.scene = new THREE.Scene (); + + this.InitCamera (); + this.InitLights (); + + this.Render (); + } + + SetClickHandler (onClick) + { + this.navigation.SetClickHandler (onClick); + } + + GetCamera () + { + return this.navigation.GetCamera (); + } + + SetCamera (camera) + { + this.navigation.SetCamera (camera); + this.Render (); + } + + Resize (width, height) + { + let innerSize = OV.GetInnerDimensions (this.canvas, width, height); + this.camera.aspect = innerSize.width / innerSize.height; + this.camera.updateProjectionMatrix (); + this.renderer.setSize (innerSize.width, innerSize.height); + this.Render (); + } + + FitToWindow (boundingSphere, animation) + { + if (boundingSphere === null) { + return; + } + let center = new OV.Coord3D (boundingSphere.center.x, boundingSphere.center.y, boundingSphere.center.z); + let radius = boundingSphere.radius; + let fov = this.camera.fov; + if (animation) { + let newCamera = this.navigation.GetFitToSphereCamera (center, radius, fov); + this.navigation.MoveCamera (newCamera, this.settings.animationSteps); + } else { + this.navigation.FitToSphere (center, radius, fov); + } + } + + AdjustClippingPlanes (boundingSphere) + { + if (boundingSphere === null) { + return; + } + if (boundingSphere.radius < 10.0) { + this.camera.near = 0.01; + this.camera.far = 100.0; + } else if (boundingSphere.radius < 100.0) { + this.camera.near = 0.1; + this.camera.far = 1000.0; + } else if (boundingSphere.radius < 1000.0) { + this.camera.near = 10.0; + this.camera.far = 10000.0; + } else { + this.camera.near = 100.0; + this.camera.far = 1000000.0; + } + this.camera.updateProjectionMatrix (); + this.Render (); + } + + IsFixUpVector () + { + return this.navigation.IsFixUpVector (); + } + + SetFixUpVector (fixUpVector) + { + let oldCamera = this.navigation.GetCamera (); + let newCamera = this.upVector.SetFixed (fixUpVector, oldCamera); + this.navigation.SetFixUpVector (fixUpVector); + if (newCamera !== null) { + this.navigation.MoveCamera (newCamera, this.settings.animationSteps); + } + this.Render (); + } + + SetUpVector (upDirection, animate) + { + let oldCamera = this.navigation.GetCamera (); + let newCamera = this.upVector.SetDirection (upDirection, oldCamera); + let animationSteps = animate ? this.settings.animationSteps : 0; + this.navigation.MoveCamera (newCamera, animationSteps); + this.Render (); + } + + FlipUpVector () + { + let oldCamera = this.navigation.GetCamera (); + let newCamera = this.upVector.Flip (oldCamera); + this.navigation.MoveCamera (newCamera, 0); + this.Render (); + } + + Render () + { + let navigationCamera = this.navigation.GetCamera (); + this.camera.position.set (navigationCamera.eye.x, navigationCamera.eye.y, navigationCamera.eye.z); + this.camera.up.set (navigationCamera.up.x, navigationCamera.up.y, navigationCamera.up.z); + this.camera.lookAt (new THREE.Vector3 (navigationCamera.center.x, navigationCamera.center.y, navigationCamera.center.z)); + + let lightDir = OV.SubCoord3D (navigationCamera.eye, navigationCamera.center); + this.light.position.set (lightDir.x, lightDir.y, lightDir.z); + this.renderer.render (this.scene, this.camera); + } + + AddMeshes (meshes) + { + for (let i = 0; i < meshes.length; i++) { + let mesh = meshes[i]; + this.scene.add (mesh); + } + this.Render (); + } + + Clear () + { + this.ClearMeshes (); + this.Render (); + } + + SetMeshesVisibility (isVisible) + { + this.EnumerateMeshes (function (mesh) { + let visible = isVisible (mesh.userData); + if (mesh.visible !== visible) { + mesh.visible = visible; + } + }); + this.Render (); + } + + SetMeshesHighlight (highlightMaterial, isHighlighted) + { + function CreateHighlightMaterials (originalMaterials, highlightMaterial) + { + let highlightMaterials = []; + for (let i = 0; i < originalMaterials.length; i++) { + highlightMaterials.push (highlightMaterial); + } + return highlightMaterials; + } + + this.EnumerateMeshes (function (mesh) { + let highlighted = isHighlighted (mesh.userData); + if (highlighted) { + if (mesh.userData.threeMaterials === null) { + mesh.userData.threeMaterials = mesh.material; + mesh.material = CreateHighlightMaterials (mesh.material, highlightMaterial); + } + } else { + if (mesh.userData.threeMaterials !== null) { + mesh.material = mesh.userData.threeMaterials; + mesh.userData.threeMaterials = null; + } + } + }); + this.Render (); + } + + GetMeshUnderMouse (mouseCoords) + { + let raycaster = new THREE.Raycaster (); + let mousePos = new THREE.Vector2 (); + mousePos.x = (mouseCoords.x / this.canvas.width) * 2 - 1; + mousePos.y = -(mouseCoords.y / this.canvas.height) * 2 + 1; + raycaster.setFromCamera (mousePos, this.camera); + let iSectObjects = raycaster.intersectObjects (this.scene.children); + for (let i = 0; i < iSectObjects.length; i++) { + let iSectObject = iSectObjects[i]; + if (iSectObject.object.type === 'Mesh' && iSectObject.object.visible) { + return iSectObject.object.userData; + } + } + return null; + } + + GetBoundingBox (needToProcess) + { + let hasMesh = false; + let boundingBox = new THREE.Box3 (); + this.EnumerateMeshes (function (mesh) { + if (needToProcess (mesh.userData)) { + boundingBox.union (new THREE.Box3 ().setFromObject (mesh)); + hasMesh = true; + } + }); + + if (!hasMesh) { + return null; + } + return boundingBox; + } + + GetBoundingSphere (needToProcess) + { + let boundingBox = this.GetBoundingBox (needToProcess); + if (boundingBox === null) { + return null; + } + + let boundingSphere = new THREE.Sphere (); + boundingBox.getBoundingSphere (boundingSphere); + return boundingSphere; + } + + EnumerateMeshesUserData (enumerator) + { + this.EnumerateMeshes (function (mesh) { + enumerator (mesh.userData); + }); + } + + EnumerateMeshes (enumerator) + { + this.scene.traverse (function (object) { + if (object.isMesh) { + enumerator (object); + } + }); + } + + ClearMeshes () + { + for (let i = this.scene.children.length - 1; i >= 0; i--) { + let object = this.scene.children[i]; + if (object.isMesh) { + object.geometry.dispose (); + this.scene.remove (object); + } + } + } + + InitCamera () + { + this.camera = new THREE.PerspectiveCamera (45.0, this.canvas.width / this.canvas.height, 0.1, 1000.0); + this.scene.add (this.camera); + + let canvasElem = this.renderer.domElement; + let camera = OV.GetDefaultCamera (OV.Direction.Z); + + let obj = this; + this.navigation = new OV.Navigation (canvasElem, camera); + this.navigation.SetUpdateHandler (function () { + obj.Render (); + }); + + this.upVector = new OV.UpVector (); + } + + InitLights () + { + let ambientLight = new THREE.AmbientLight (0x888888); + this.scene.add (ambientLight); + + this.light = new THREE.DirectionalLight (0x888888); + this.scene.add (this.light); + } +}; diff --git a/test/framework/native_import.js b/test/framework/native_import.js new file mode 100644 index 0000000..214ff73 --- /dev/null +++ b/test/framework/native_import.js @@ -0,0 +1,14 @@ +module.exports = +{ + AddNativeSourceFile : function (fileName) + { + var path = require ('path'); + var rootDir = path.join (__dirname, '..'); + var fullPath = path.resolve (rootDir, fileName); + + var fs = require ('fs'); + var content = fs.readFileSync (fullPath).toString (); + + eval.apply (global, [content]); + } +} diff --git a/test/package.json b/test/package.json new file mode 100644 index 0000000..a91072a --- /dev/null +++ b/test/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "mocha": "*" + }, + "scripts": { + "test": "mocha" + } +} diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..4f1541c --- /dev/null +++ b/test/test.js @@ -0,0 +1,27 @@ +var fs = require ('fs'); +process.chdir (__dirname); + +global.atob = function (base64String) { + return Buffer.from (base64String, 'base64').toString('binary') +}; + +global.Blob = function () { + +}; + +global.URL = { + createObjectURL : function () { + return 'ObjectUrl'; + } +} + +require ('./utils/importall.js'); +var testDirName = './tests/'; +var files = fs.readdirSync (testDirName, { withFileTypes: true }); +var i, file; +for (i = 0; i < files.length; i++) { + file = files[i]; + if (file.isFile ()) { + require (testDirName + file.name); + } +} diff --git a/test/testfiles/3ds/cube_four_instances.3ds b/test/testfiles/3ds/cube_four_instances.3ds new file mode 100644 index 0000000..eeb18d2 Binary files /dev/null and b/test/testfiles/3ds/cube_four_instances.3ds differ diff --git a/test/testfiles/3ds/cube_instance_scale_rotation.3ds b/test/testfiles/3ds/cube_instance_scale_rotation.3ds new file mode 100644 index 0000000..0d4f10e Binary files /dev/null and b/test/testfiles/3ds/cube_instance_scale_rotation.3ds differ diff --git a/test/testfiles/3ds/cube_two_instances.3ds b/test/testfiles/3ds/cube_two_instances.3ds new file mode 100644 index 0000000..3745081 Binary files /dev/null and b/test/testfiles/3ds/cube_two_instances.3ds differ diff --git a/test/testfiles/3ds/cube_with_materials.3ds b/test/testfiles/3ds/cube_with_materials.3ds new file mode 100644 index 0000000..04c6d0e Binary files /dev/null and b/test/testfiles/3ds/cube_with_materials.3ds differ diff --git a/test/testfiles/3ds/cube_with_texture_transformations.3ds b/test/testfiles/3ds/cube_with_texture_transformations.3ds new file mode 100644 index 0000000..70d02d3 Binary files /dev/null and b/test/testfiles/3ds/cube_with_texture_transformations.3ds differ diff --git a/test/testfiles/3ds/texture.png b/test/testfiles/3ds/texture.png new file mode 100644 index 0000000..29ce540 Binary files /dev/null and b/test/testfiles/3ds/texture.png differ diff --git a/test/testfiles/bin/binary_content.bin b/test/testfiles/bin/binary_content.bin new file mode 100644 index 0000000..39f984d Binary files /dev/null and b/test/testfiles/bin/binary_content.bin differ diff --git a/test/testfiles/bin/binary_content.txt b/test/testfiles/bin/binary_content.txt new file mode 100644 index 0000000..291c940 --- /dev/null +++ b/test/testfiles/bin/binary_content.txt @@ -0,0 +1,46 @@ +1 (bool, 1) +0 (bool, 1) +a (char, 1) +A (char, 1) +a (unsigned char, 1) +A (unsigned char, 1) +42 (short, 2) +-42 (short, 2) +32000 (short, 2) +-32000 (short, 2) +42 (unsigned short, 2) +65494 (unsigned short, 2) +32000 (unsigned short, 2) +33536 (unsigned short, 2) +42 (int, 4) +-42 (int, 4) +32000 (int, 4) +-32000 (int, 4) +2000000000 (int, 4) +-2000000000 (int, 4) +42 (unsigned int, 4) +4294967254 (unsigned int, 4) +32000 (unsigned int, 4) +4294935296 (unsigned int, 4) +2000000000 (unsigned int, 4) +2294967296 (unsigned int, 4) +42 (long, 4) +-42 (long, 4) +32000 (long, 4) +-32000 (long, 4) +2000000000 (long, 4) +-2000000000 (long, 4) +42 (unsigned long, 4) +4294967254 (unsigned long, 4) +32000 (unsigned long, 4) +4294935296 (unsigned long, 4) +2000000000 (unsigned long, 4) +2294967296 (unsigned long, 4) +42 (float, 4) +-42 (float, 4) +12345.7 (float, 4) +-12345.7 (float, 4) +42 (double, 8) +-42 (double, 8) +12345.7 (double, 8) +-12345.7 (double, 8) diff --git a/test/testfiles/bin/binarywriter.cpp b/test/testfiles/bin/binarywriter.cpp new file mode 100644 index 0000000..f34bcec --- /dev/null +++ b/test/testfiles/bin/binarywriter.cpp @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +std::string DemangleTypeName (const char* name) +{ + int status = -4; + char* demangled = abi::__cxa_demangle (name, NULL, NULL, &status); + std::string result; + if (status == 0) { + result = demangled; + } else { + result = name; + } + delete[] demangled; + return result; +} + +class BinaryWriter +{ +public: + BinaryWriter (const std::string& logFileName, const std::string& binaryFileName) + { + logFile.open (logFileName.c_str (), std::ofstream::out); + binaryFile.open (binaryFileName.c_str (), std::ofstream::binary); + } + + ~BinaryWriter () + { + logFile.close (); + binaryFile.close (); + } + + template + void Write (Type value) + { + logFile << value << " (" << DemangleTypeName (typeid (value).name ()) << ", " << sizeof (value) << ")" << std::endl; + binaryFile.write ((char*) &value, sizeof (value)); + } + +private: + std::ofstream logFile; + std::ofstream binaryFile; +}; + +int main () +{ + BinaryWriter bw ("result.txt", "result.bin"); + + bw.Write ((bool) true); + bw.Write ((bool) false); + + bw.Write ((char) 'a'); + bw.Write ((char) 'A'); + bw.Write ((unsigned char) 'a'); + bw.Write ((unsigned char) 'A'); + + bw.Write ((short) 42); + bw.Write ((short) -42); + bw.Write ((short) 32000); + bw.Write ((short) -32000); + bw.Write ((unsigned short) 42); + bw.Write ((unsigned short) -42); + bw.Write ((unsigned short) 32000); + bw.Write ((unsigned short) -32000); + + bw.Write ((int) 42); + bw.Write ((int) -42); + bw.Write ((int) 32000); + bw.Write ((int) -32000); + bw.Write ((int) 2000000000); + bw.Write ((int) -2000000000); + bw.Write ((unsigned int) 42); + bw.Write ((unsigned int) -42); + bw.Write ((unsigned int) 32000); + bw.Write ((unsigned int) -32000); + bw.Write ((unsigned int) 2000000000); + bw.Write ((unsigned int) -2000000000); + + bw.Write ((long) 42); + bw.Write ((long) -42); + bw.Write ((long) 32000); + bw.Write ((long) -32000); + bw.Write ((long) 2000000000); + bw.Write ((long) -2000000000); + bw.Write ((unsigned long) 42); + bw.Write ((unsigned long) -42); + bw.Write ((unsigned long) 32000); + bw.Write ((unsigned long) -32000); + bw.Write ((unsigned long) 2000000000); + bw.Write ((unsigned long) -2000000000); + + bw.Write ((float) 42); + bw.Write ((float) -42); + bw.Write ((float) 12345.6789); + bw.Write ((float) -12345.6789); + + bw.Write ((double) 42); + bw.Write ((double) -42); + bw.Write ((double) 12345.6789); + bw.Write ((double) -12345.6789); + + return 0; +} diff --git a/test/testfiles/gltf/Box/README.md b/test/testfiles/gltf/Box/README.md new file mode 100644 index 0000000..ffb95a3 --- /dev/null +++ b/test/testfiles/gltf/Box/README.md @@ -0,0 +1,10 @@ +# Box +## Screenshot + +![screenshot](screenshot/screenshot.png) + +## License Information + +Donated by [Cesium](http://cesiumjs.org/) for glTF testing. + +This model is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/). diff --git a/test/testfiles/gltf/Box/glTF-Binary/Box.glb b/test/testfiles/gltf/Box/glTF-Binary/Box.glb new file mode 100644 index 0000000..95ec886 Binary files /dev/null and b/test/testfiles/gltf/Box/glTF-Binary/Box.glb differ diff --git a/test/testfiles/gltf/Box/glTF-Draco/Box.bin b/test/testfiles/gltf/Box/glTF-Draco/Box.bin new file mode 100644 index 0000000..bd2481d Binary files /dev/null and b/test/testfiles/gltf/Box/glTF-Draco/Box.bin differ diff --git a/test/testfiles/gltf/Box/glTF-Draco/Box.gltf b/test/testfiles/gltf/Box/glTF-Draco/Box.gltf new file mode 100644 index 0000000..d38bc8c --- /dev/null +++ b/test/testfiles/gltf/Box/glTF-Draco/Box.gltf @@ -0,0 +1,152 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 0, + -1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 2 + }, + "indices": 0, + "mode": 4, + "material": 0, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 0, + "attributes": { + "NORMAL": 0, + "POSITION": 1 + } + } + } + } + ], + "name": "Mesh" + } + ], + "accessors": [ + { + "componentType": 5123, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 24, + "max": [ + 1.007843137254902, + 1.007843137254902, + 1.007843137254902 + ], + "min": [ + -1.007843137254902, + -1.007843137254902, + -1.007843137254902 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 24, + "max": [ + 0.5004885197850513, + 0.5004885197850513, + 0.5004885197850513 + ], + "min": [ + -0.5004885197850513, + -0.5004885197850513, + -0.5004885197850513 + ], + "type": "VEC3" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.800000011920929, + 0, + 0, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 1 + }, + "name": "Red", + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "doubleSided": false + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 118 + } + ], + "buffers": [ + { + "name": "Box", + "byteLength": 120, + "uri": "Box.bin" + } + ], + "extensionsRequired": [ + "KHR_draco_mesh_compression" + ], + "extensionsUsed": [ + "KHR_draco_mesh_compression" + ] +} diff --git a/test/testfiles/gltf/Box/glTF-Embedded/Box.gltf b/test/testfiles/gltf/Box/glTF-Embedded/Box.gltf new file mode 100644 index 0000000..ea0e8ad --- /dev/null +++ b/test/testfiles/gltf/Box/glTF-Embedded/Box.gltf @@ -0,0 +1,142 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 2 + }, + "indices": 0, + "mode": 4, + "material": 0 + } + ], + "name": "Mesh" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 288, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.800000011920929, + 0.0, + 0.0, + 1.0 + ], + "metallicFactor": 0.0 + }, + "name": "Red" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 576, + "byteLength": 72, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 576, + "byteStride": 12, + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 648, + "uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAC/AAAAvwAAAL8AAAC/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAvwAAAD8AAAC/AAAAPwAAAD8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAPwAAAD8AAAC/AAABAAIAAwACAAEABAAFAAYABwAGAAUACAAJAAoACwAKAAkADAANAA4ADwAOAA0AEAARABIAEwASABEAFAAVABYAFwAWABUA" + } + ] +} diff --git a/test/testfiles/gltf/Box/glTF/Box.gltf b/test/testfiles/gltf/Box/glTF/Box.gltf new file mode 100644 index 0000000..7f603f0 --- /dev/null +++ b/test/testfiles/gltf/Box/glTF/Box.gltf @@ -0,0 +1,142 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 2 + }, + "indices": 0, + "mode": 4, + "material": 0 + } + ], + "name": "Mesh" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 288, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.800000011920929, + 0.0, + 0.0, + 1.0 + ], + "metallicFactor": 0.0 + }, + "name": "Red" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 576, + "byteLength": 72, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 576, + "byteStride": 12, + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 648, + "uri": "Box0.bin" + } + ] +} diff --git a/test/testfiles/gltf/Box/glTF/Box0.bin b/test/testfiles/gltf/Box/glTF/Box0.bin new file mode 100644 index 0000000..d7798ab Binary files /dev/null and b/test/testfiles/gltf/Box/glTF/Box0.bin differ diff --git a/test/testfiles/gltf/Box/screenshot/screenshot.png b/test/testfiles/gltf/Box/screenshot/screenshot.png new file mode 100644 index 0000000..b74e785 Binary files /dev/null and b/test/testfiles/gltf/Box/screenshot/screenshot.png differ diff --git a/test/testfiles/gltf/BoxInterleaved/README.md b/test/testfiles/gltf/BoxInterleaved/README.md new file mode 100644 index 0000000..76b1518 --- /dev/null +++ b/test/testfiles/gltf/BoxInterleaved/README.md @@ -0,0 +1,10 @@ +# Box with interleaved position and normal attributes +## Screenshot + +![screenshot](screenshot/screenshot.png) + +## License Information + +Donated by [Cesium](http://cesiumjs.org/) and interleaved by [AngeloReppucci](https://github.com/AngeloReppucci) for glTF testing. + +This model is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/). diff --git a/test/testfiles/gltf/BoxInterleaved/glTF-Binary/BoxInterleaved.glb b/test/testfiles/gltf/BoxInterleaved/glTF-Binary/BoxInterleaved.glb new file mode 100644 index 0000000..25cb404 Binary files /dev/null and b/test/testfiles/gltf/BoxInterleaved/glTF-Binary/BoxInterleaved.glb differ diff --git a/test/testfiles/gltf/BoxInterleaved/glTF-Embedded/BoxInterleaved.gltf b/test/testfiles/gltf/BoxInterleaved/glTF-Embedded/BoxInterleaved.gltf new file mode 100644 index 0000000..48f80e9 --- /dev/null +++ b/test/testfiles/gltf/BoxInterleaved/glTF-Embedded/BoxInterleaved.gltf @@ -0,0 +1,140 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 2 + }, + "indices": 0, + "mode": 4, + "material": 0 + } + ], + "name": "Mesh" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 12, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.800000011920929, + 0.0, + 0.0, + 1.0 + ] + } + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 576, + "byteLength": 72, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 576, + "byteStride": 24, + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 648, + "uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAIA/AAAAvwAAAL8AAAA/AAAAAAAAAAAAAIA/AAAAPwAAAL8AAAA/AAAAAAAAAAAAAIA/AAAAvwAAAD8AAAA/AAAAAAAAAAAAAIA/AAAAPwAAAD8AAAA/AAAAAAAAgL8AAAAAAAAAPwAAAL8AAAA/AAAAAAAAgL8AAAAAAAAAvwAAAL8AAAA/AAAAAAAAgL8AAAAAAAAAPwAAAL8AAAC/AAAAAAAAgL8AAAAAAAAAvwAAAL8AAAC/AACAPwAAAAAAAAAAAAAAPwAAAD8AAAA/AACAPwAAAAAAAAAAAAAAPwAAAL8AAAA/AACAPwAAAAAAAAAAAAAAPwAAAD8AAAC/AACAPwAAAAAAAAAAAAAAPwAAAL8AAAC/AAAAAAAAgD8AAAAAAAAAvwAAAD8AAAA/AAAAAAAAgD8AAAAAAAAAPwAAAD8AAAA/AAAAAAAAgD8AAAAAAAAAvwAAAD8AAAC/AAAAAAAAgD8AAAAAAAAAPwAAAD8AAAC/AACAvwAAAAAAAAAAAAAAvwAAAL8AAAA/AACAvwAAAAAAAAAAAAAAvwAAAD8AAAA/AACAvwAAAAAAAAAAAAAAvwAAAL8AAAC/AACAvwAAAAAAAAAAAAAAvwAAAD8AAAC/AAAAAAAAAAAAAIC/AAAAvwAAAL8AAAC/AAAAAAAAAAAAAIC/AAAAvwAAAD8AAAC/AAAAAAAAAAAAAIC/AAAAPwAAAL8AAAC/AAAAAAAAAAAAAIC/AAAAPwAAAD8AAAC/AAABAAIAAwACAAEABAAFAAYABwAGAAUACAAJAAoACwAKAAkADAANAA4ADwAOAA0AEAARABIAEwASABEAFAAVABYAFwAWABUA" + } + ] +} diff --git a/test/testfiles/gltf/BoxInterleaved/glTF/BoxInterleaved.bin b/test/testfiles/gltf/BoxInterleaved/glTF/BoxInterleaved.bin new file mode 100644 index 0000000..c046b77 Binary files /dev/null and b/test/testfiles/gltf/BoxInterleaved/glTF/BoxInterleaved.bin differ diff --git a/test/testfiles/gltf/BoxInterleaved/glTF/BoxInterleaved.gltf b/test/testfiles/gltf/BoxInterleaved/glTF/BoxInterleaved.gltf new file mode 100644 index 0000000..9e94c53 --- /dev/null +++ b/test/testfiles/gltf/BoxInterleaved/glTF/BoxInterleaved.gltf @@ -0,0 +1,140 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 2 + }, + "indices": 0, + "mode": 4, + "material": 0 + } + ], + "name": "Mesh" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 12, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.800000011920929, + 0.0, + 0.0, + 1.0 + ] + } + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 576, + "byteLength": 72, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 576, + "byteStride": 24, + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 648, + "uri": "BoxInterleaved.bin" + } + ] +} diff --git a/test/testfiles/gltf/BoxInterleaved/screenshot/screenshot.png b/test/testfiles/gltf/BoxInterleaved/screenshot/screenshot.png new file mode 100644 index 0000000..b74e785 Binary files /dev/null and b/test/testfiles/gltf/BoxInterleaved/screenshot/screenshot.png differ diff --git a/test/testfiles/gltf/BoxTextured/README.md b/test/testfiles/gltf/BoxTextured/README.md new file mode 100644 index 0000000..89c9c65 --- /dev/null +++ b/test/testfiles/gltf/BoxTextured/README.md @@ -0,0 +1,10 @@ +# Box Textured +## Screenshot + +![screenshot](screenshot/screenshot.png) + +## License Information + +Donated by Cesium for glTF testing. Please follow the [Cesium Trademark Terms and Conditions](https://github.com/AnalyticalGraphicsInc/cesium/wiki/CesiumTrademark.pdf). + +This model is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/). diff --git a/test/testfiles/gltf/BoxTextured/glTF-Binary/BoxTextured.glb b/test/testfiles/gltf/BoxTextured/glTF-Binary/BoxTextured.glb new file mode 100644 index 0000000..5dae158 Binary files /dev/null and b/test/testfiles/gltf/BoxTextured/glTF-Binary/BoxTextured.glb differ diff --git a/test/testfiles/gltf/BoxTextured/glTF-Embedded/BoxTextured.gltf b/test/testfiles/gltf/BoxTextured/glTF-Embedded/BoxTextured.gltf new file mode 100644 index 0000000..4a6ab9c --- /dev/null +++ b/test/testfiles/gltf/BoxTextured/glTF-Embedded/BoxTextured.gltf @@ -0,0 +1,181 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 2, + "TEXCOORD_0": 3 + }, + "indices": 0, + "mode": 4, + "material": 0 + } + ], + "name": "Mesh" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 288, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 6.0, + 1.0 + ], + "min": [ + 0.0, + 0.0 + ], + "type": "VEC2" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicFactor": 0.0 + }, + "name": "Texture" + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + } + ], + "images": [ + { + "uri": "data:image/png;base64,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" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9986, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 768, + "byteLength": 72, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 576, + "byteStride": 12, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 576, + "byteLength": 192, + "byteStride": 8, + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 840, + "uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAD8AAAA/AAAAvwAAAD8AAAC/AAAAPwAAAD8AAAC/AAAAPwAAAL8AAAA/AAAAvwAAAL8AAAA/AAAAPwAAAL8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAA/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAvwAAAL8AAAC/AAAAvwAAAD8AAAC/AAAAPwAAAL8AAAC/AAAAPwAAAD8AAAC/AADAQAAAAAAAAKBAAAAAAAAAwED+/38/AACgQP7/fz8AAIBAAAAAAAAAoEAAAAAAAACAQAAAgD8AAKBAAACAPwAAAEAAAAAAAACAPwAAAAAAAABAAACAPwAAgD8AAIA/AABAQAAAAAAAAIBAAAAAAAAAQEAAAIA/AACAQAAAgD8AAEBAAAAAAAAAAEAAAAAAAABAQAAAgD8AAABAAACAPwAAAAAAAAAAAAAAAP7/fz8AAIA/AAAAAAAAgD/+/38/AAABAAIAAwACAAEABAAFAAYABwAGAAUACAAJAAoACwAKAAkADAANAA4ADwAOAA0AEAARABIAEwASABEAFAAVABYAFwAWABUA" + } + ] +} diff --git a/test/testfiles/gltf/BoxTextured/glTF/BoxTextured.gltf b/test/testfiles/gltf/BoxTextured/glTF/BoxTextured.gltf new file mode 100644 index 0000000..eff658f --- /dev/null +++ b/test/testfiles/gltf/BoxTextured/glTF/BoxTextured.gltf @@ -0,0 +1,181 @@ +{ + "asset": { + "generator": "COLLADA2GLTF", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "matrix": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ] + }, + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "NORMAL": 1, + "POSITION": 2, + "TEXCOORD_0": 3 + }, + "indices": 0, + "mode": 4, + "material": 0 + } + ], + "name": "Mesh" + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5123, + "count": 36, + "max": [ + 23 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 1.0, + 1.0, + 1.0 + ], + "min": [ + -1.0, + -1.0, + -1.0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 288, + "componentType": 5126, + "count": 24, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + { + "bufferView": 2, + "byteOffset": 0, + "componentType": 5126, + "count": 24, + "max": [ + 6.0, + 1.0 + ], + "min": [ + 0.0, + 0.0 + ], + "type": "VEC2" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicFactor": 0.0 + }, + "name": "Texture" + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + } + ], + "images": [ + { + "uri": "CesiumLogoFlat.png" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9986, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 768, + "byteLength": 72, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 576, + "byteStride": 12, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 576, + "byteLength": 192, + "byteStride": 8, + "target": 34962 + } + ], + "buffers": [ + { + "byteLength": 840, + "uri": "BoxTextured0.bin" + } + ] +} diff --git a/test/testfiles/gltf/BoxTextured/glTF/BoxTextured0.bin b/test/testfiles/gltf/BoxTextured/glTF/BoxTextured0.bin new file mode 100644 index 0000000..d2a7355 Binary files /dev/null and b/test/testfiles/gltf/BoxTextured/glTF/BoxTextured0.bin differ diff --git a/test/testfiles/gltf/BoxTextured/glTF/CesiumLogoFlat.png b/test/testfiles/gltf/BoxTextured/glTF/CesiumLogoFlat.png new file mode 100644 index 0000000..8159c4c Binary files /dev/null and b/test/testfiles/gltf/BoxTextured/glTF/CesiumLogoFlat.png differ diff --git a/test/testfiles/gltf/BoxTextured/screenshot/screenshot.png b/test/testfiles/gltf/BoxTextured/screenshot/screenshot.png new file mode 100644 index 0000000..47c5d4c Binary files /dev/null and b/test/testfiles/gltf/BoxTextured/screenshot/screenshot.png differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/LICENSE b/test/testfiles/gltf/Mesh_PrimitiveMode/LICENSE new file mode 100644 index 0000000..8bf601d --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Khronos Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.bin new file mode 100644 index 0000000..29c7c4b Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.gltf new file mode 100644 index 0000000..528ea8c --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.gltf @@ -0,0 +1,71 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 1024, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_00.bin", + "byteLength": 12288 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 12288, + "name": "Positions" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "material": 0, + "mode": 0 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.bin new file mode 100644 index 0000000..6e4a705 Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.gltf new file mode 100644 index 0000000..8266cda --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.gltf @@ -0,0 +1,71 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 8, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_01.bin", + "byteLength": 96 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 96, + "name": "Positions" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "material": 0, + "mode": 1 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.bin new file mode 100644 index 0000000..6394c88 Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.gltf new file mode 100644 index 0000000..d22a91e --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.gltf @@ -0,0 +1,71 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_02.bin", + "byteLength": 48 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 48, + "name": "Positions" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "material": 0, + "mode": 2 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.bin new file mode 100644 index 0000000..d2ae892 Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.gltf new file mode 100644 index 0000000..36b03ca --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.gltf @@ -0,0 +1,71 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 5, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_03.bin", + "byteLength": 60 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 60, + "name": "Positions" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "material": 0, + "mode": 3 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.bin new file mode 100644 index 0000000..a2e5d56 Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.gltf new file mode 100644 index 0000000..7a1b67d --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.gltf @@ -0,0 +1,71 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_04.bin", + "byteLength": 48 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 48, + "name": "Positions" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "material": 0, + "mode": 5 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.bin new file mode 100644 index 0000000..c16679d Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.gltf new file mode 100644 index 0000000..ffee47a --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.gltf @@ -0,0 +1,71 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_05.bin", + "byteLength": 48 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 48, + "name": "Positions" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "material": 0, + "mode": 6 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.bin new file mode 100644 index 0000000..867693d Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.gltf new file mode 100644 index 0000000..87fa145 --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.gltf @@ -0,0 +1,70 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 6, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_06.bin", + "byteLength": 72 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 72, + "name": "Positions" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "material": 0 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.bin new file mode 100644 index 0000000..cfc7d5d Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.gltf new file mode 100644 index 0000000..8b3bbaf --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.gltf @@ -0,0 +1,85 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 1024, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + }, + { + "bufferView": 1, + "componentType": 5125, + "count": 1024, + "type": "SCALAR", + "name": "Indices Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_07.bin", + "byteLength": 16384 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 12288, + "name": "Positions" + }, + { + "buffer": 0, + "byteOffset": 12288, + "byteLength": 4096, + "name": "Indices" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "material": 0, + "mode": 0 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.bin new file mode 100644 index 0000000..77d730d Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.gltf new file mode 100644 index 0000000..6696ff3 --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.gltf @@ -0,0 +1,85 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + }, + { + "bufferView": 1, + "componentType": 5125, + "count": 8, + "type": "SCALAR", + "name": "Indices Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_08.bin", + "byteLength": 80 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 48, + "name": "Positions" + }, + { + "buffer": 0, + "byteOffset": 48, + "byteLength": 32, + "name": "Indices" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "material": 0, + "mode": 1 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.bin new file mode 100644 index 0000000..b828a7a Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.gltf new file mode 100644 index 0000000..39fe227 --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.gltf @@ -0,0 +1,85 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + }, + { + "bufferView": 1, + "componentType": 5125, + "count": 4, + "type": "SCALAR", + "name": "Indices Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_09.bin", + "byteLength": 64 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 48, + "name": "Positions" + }, + { + "buffer": 0, + "byteOffset": 48, + "byteLength": 16, + "name": "Indices" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "material": 0, + "mode": 2 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.bin new file mode 100644 index 0000000..eb4e3f8 Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.gltf new file mode 100644 index 0000000..4a4cb2e --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.gltf @@ -0,0 +1,85 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + }, + { + "bufferView": 1, + "componentType": 5125, + "count": 5, + "type": "SCALAR", + "name": "Indices Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_10.bin", + "byteLength": 68 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 48, + "name": "Positions" + }, + { + "buffer": 0, + "byteOffset": 48, + "byteLength": 20, + "name": "Indices" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "material": 0, + "mode": 3 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.bin new file mode 100644 index 0000000..cf641b2 Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.gltf new file mode 100644 index 0000000..b7352bf --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.gltf @@ -0,0 +1,85 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + }, + { + "bufferView": 1, + "componentType": 5125, + "count": 4, + "type": "SCALAR", + "name": "Indices Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_11.bin", + "byteLength": 64 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 48, + "name": "Positions" + }, + { + "buffer": 0, + "byteOffset": 48, + "byteLength": 16, + "name": "Indices" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "material": 0, + "mode": 5 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.bin new file mode 100644 index 0000000..f0a45dc Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.gltf new file mode 100644 index 0000000..51acab9 --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.gltf @@ -0,0 +1,85 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + }, + { + "bufferView": 1, + "componentType": 5125, + "count": 4, + "type": "SCALAR", + "name": "Indices Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_12.bin", + "byteLength": 64 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 48, + "name": "Positions" + }, + { + "buffer": 0, + "byteOffset": 48, + "byteLength": 16, + "name": "Indices" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "material": 0, + "mode": 6 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_13.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_13.bin new file mode 100644 index 0000000..c3bea4f Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_13.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_13.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_13.gltf new file mode 100644 index 0000000..c2b1af1 --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_13.gltf @@ -0,0 +1,84 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + }, + { + "bufferView": 1, + "componentType": 5125, + "count": 6, + "type": "SCALAR", + "name": "Indices Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_13.bin", + "byteLength": 72 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 48, + "name": "Positions" + }, + { + "buffer": 0, + "byteOffset": 48, + "byteLength": 24, + "name": "Indices" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "material": 0 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_14.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_14.bin new file mode 100644 index 0000000..e25c8cd Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_14.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_14.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_14.gltf new file mode 100644 index 0000000..fce1f8d --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_14.gltf @@ -0,0 +1,84 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + }, + { + "bufferView": 1, + "componentType": 5121, + "count": 6, + "type": "SCALAR", + "name": "Indices Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_14.bin", + "byteLength": 54 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 48, + "name": "Positions" + }, + { + "buffer": 0, + "byteOffset": 48, + "byteLength": 6, + "name": "Indices" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "material": 0 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_15.bin b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_15.bin new file mode 100644 index 0000000..9968347 Binary files /dev/null and b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_15.bin differ diff --git a/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_15.gltf b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_15.gltf new file mode 100644 index 0000000..a688e29 --- /dev/null +++ b/test/testfiles/gltf/Mesh_PrimitiveMode/Mesh_PrimitiveMode_15.gltf @@ -0,0 +1,84 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions Accessor" + }, + { + "bufferView": 1, + "componentType": 5123, + "count": 6, + "type": "SCALAR", + "name": "Indices Accessor" + } + ], + "asset": { + "generator": "glTF Asset Generator", + "version": "2.0" + }, + "buffers": [ + { + "uri": "Mesh_PrimitiveMode_15.bin", + "byteLength": 60 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 48, + "name": "Positions" + }, + { + "buffer": 0, + "byteOffset": 48, + "byteLength": 12, + "name": "Indices" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "metallicFactor": 0.0 + } + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "material": 0 + } + ] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/gltf/OrientationTest/README.md b/test/testfiles/gltf/OrientationTest/README.md new file mode 100644 index 0000000..0816d68 --- /dev/null +++ b/test/testfiles/gltf/OrientationTest/README.md @@ -0,0 +1,27 @@ +# Orientation Test + +## Screenshot + +![screenshot](screenshot/screenshot.png) + +## Description + +This model tests the orientation supplied by nodes in the hierarchy. The red, green, and blue arrows test X, Y, and Z rotations respectively expressed as quaternions in the nodes. The cyan, magenta, and yellow arrows test X, Y, and Z rotations respectively expressed with rotation matrices in the nodes. + +Each arrow has a target indicator of the same color, opposite the point. These target indicators have their positions and orientations baked into their vertex data, so do not rely on node transformations to appear correct. + +Thus, if each colored arrow points directly at a target of the same color, the test passes. If an arrow appears to have been rotated away from the target of the same color, it could indicate a misplaced matrix transpose or some other orientation problem. + +## Sample Test Failure + +![Sample Test Failure](screenshot/OrientationTestFail.png) + +This failed render shows what happens if the rotation quaternions are converted to transposed rotations (in this case, converted to a 3x3 rotation matrix that was transposed). This same effect is caused if there is confusion between row-major and column-major matrix ordering. + +This image shows the red, green, and blue arrows, which all use rotation quaternions on the three axes. On the opposite sides of the cube, the cyan, magenta, and yellow arrows perform the same kinds of tests (with slightly different angles) using node orientation matrices in the glTF directly. Thus, it's possible that only one set or the other set of arrows will fail the test. + +## License Information + +Copyright 2018 Analytical Graphics, Inc. +CC-BY 4.0 https://creativecommons.org/licenses/by/4.0/ +Model by Ed Mackey. diff --git a/test/testfiles/gltf/OrientationTest/glTF-Binary/OrientationTest.glb b/test/testfiles/gltf/OrientationTest/glTF-Binary/OrientationTest.glb new file mode 100644 index 0000000..f8a6b7e Binary files /dev/null and b/test/testfiles/gltf/OrientationTest/glTF-Binary/OrientationTest.glb differ diff --git a/test/testfiles/gltf/OrientationTest/glTF-Embedded/OrientationTest.gltf b/test/testfiles/gltf/OrientationTest/glTF-Embedded/OrientationTest.gltf new file mode 100644 index 0000000..4c5fec7 --- /dev/null +++ b/test/testfiles/gltf/OrientationTest/glTF-Embedded/OrientationTest.gltf @@ -0,0 +1,1258 @@ +{ + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5121, + "count" : 78, + "max" : [ + 51 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 52, + "max" : [ + -0.6740907430648804, + 3.9529545307159424, + 5.33065128326416 + ], + "min" : [ + -1.3648574352264404, + 2.9005930423736572, + 4.66934871673584 + ], + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 52, + "max" : [ + 0.994742751121521, + 0.9659255146980286, + 1.0 + ], + "min" : [ + -0.8102691173553467, + -0.5860580801963806, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 3, + "componentType" : 5121, + "count" : 114, + "max" : [ + 73 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 74, + "max" : [ + 0.7157971262931824, + 3.0, + 0.33065110445022583 + ], + "min" : [ + -0.7157971262931824, + -1.0, + -0.33065110445022583 + ], + "type" : "VEC3" + }, + { + "bufferView" : 5, + "componentType" : 5126, + "count" : 74, + "max" : [ + 1.0, + 0.374779611825943, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 6, + "componentType" : 5121, + "count" : 114, + "max" : [ + 77 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 7, + "componentType" : 5126, + "count" : 78, + "max" : [ + 0.7157971262931824, + 0.33065134286880493, + 3.0 + ], + "min" : [ + -0.7157971262931824, + -0.3306511640548706, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 8, + "componentType" : 5126, + "count" : 78, + "max" : [ + 1.0, + 1.0, + 0.374779611825943 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 9, + "componentType" : 5121, + "count" : 114, + "max" : [ + 77 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 10, + "componentType" : 5126, + "count" : 78, + "max" : [ + 0.7157971262931824, + 0.33065131306648254, + 1.0 + ], + "min" : [ + -0.7157971262931824, + -0.330651193857193, + -3.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 11, + "componentType" : 5126, + "count" : 78, + "max" : [ + 1.0, + 1.0, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -0.374779611825943 + ], + "type" : "VEC3" + }, + { + "bufferView" : 12, + "componentType" : 5123, + "count" : 420, + "max" : [ + 271 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 13, + "componentType" : 5126, + "count" : 272, + "max" : [ + 5.000002384185791, + 5.0, + 5.000002861022949 + ], + "min" : [ + -5.000001907348633, + -5.0, + -5.000001907348633 + ], + "type" : "VEC3" + }, + { + "bufferView" : 14, + "componentType" : 5126, + "count" : 272, + "max" : [ + 1.0, + 1.0, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 15, + "componentType" : 5121, + "count" : 78, + "max" : [ + 49 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 16, + "componentType" : 5126, + "count" : 50, + "max" : [ + -0.46912679076194763, + -4.66934871673584, + 3.9916374683380127 + ], + "min" : [ + -1.1686336994171143, + -5.330650806427002, + 2.93727445602417 + ], + "type" : "VEC3" + }, + { + "bufferView" : 17, + "componentType" : 5126, + "count" : 50, + "max" : [ + 0.9880198836326599, + 1.0, + 0.9781473875045776 + ], + "min" : [ + -0.8398305773735046, + -1.0, + -0.542848527431488 + ], + "type" : "VEC3" + }, + { + "bufferView" : 18, + "componentType" : 5121, + "count" : 114, + "max" : [ + 77 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 19, + "componentType" : 5126, + "count" : 78, + "max" : [ + 0.3306511640548706, + 3.0, + 0.7157971262931824 + ], + "min" : [ + -0.3306511640548706, + -1.0, + -0.7157971262931824 + ], + "type" : "VEC3" + }, + { + "bufferView" : 20, + "componentType" : 5126, + "count" : 78, + "max" : [ + 1.0, + 0.374779611825943, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 21, + "componentType" : 5121, + "count" : 78, + "max" : [ + 53 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 22, + "componentType" : 5126, + "count" : 54, + "max" : [ + -4.66934871673584, + 4.039160251617432, + 0.6999828815460205 + ], + "min" : [ + -5.33065128326416, + 2.991360902786255, + -0.012430161237716675 + ], + "type" : "VEC3" + }, + { + "bufferView" : 23, + "componentType" : 5126, + "count" : 54, + "max" : [ + 1.0, + 0.9961948394775391, + 0.8997273445129395 + ], + "min" : [ + -1.0, + -0.4364525079727173, + -0.9618476033210754 + ], + "type" : "VEC3" + }, + { + "bufferView" : 24, + "componentType" : 5121, + "count" : 114, + "max" : [ + 77 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 25, + "componentType" : 5126, + "count" : 78, + "max" : [ + 0.3306511640548706, + 3.0, + 0.7157971262931824 + ], + "min" : [ + -0.3306511640548706, + -1.0, + -0.7157971262931824 + ], + "type" : "VEC3" + }, + { + "bufferView" : 26, + "componentType" : 5126, + "count" : 78, + "max" : [ + 1.0, + 0.374779611825943, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 27, + "componentType" : 5121, + "count" : 78, + "max" : [ + 51 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 28, + "componentType" : 5126, + "count" : 52, + "max" : [ + 3.864471435546875, + 5.33065128326416, + -1.0113166570663452 + ], + "min" : [ + 2.8218495845794678, + 4.669349193572998, + -1.6833229064941406 + ], + "type" : "VEC3" + }, + { + "bufferView" : 29, + "componentType" : 5126, + "count" : 52, + "max" : [ + 0.9749377965927124, + 1.0, + 0.9998826384544373 + ], + "min" : [ + -0.654447615146637, + -1.0, + -0.7971041202545166 + ], + "type" : "VEC3" + }, + { + "bufferView" : 30, + "componentType" : 5121, + "count" : 78, + "max" : [ + 49 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 31, + "componentType" : 5126, + "count" : 50, + "max" : [ + 1.4936277866363525, + 3.9211390018463135, + -4.66934871673584 + ], + "min" : [ + 0.8097413778305054, + 2.8717148303985596, + -5.33065128326416 + ], + "type" : "VEC3" + }, + { + "bufferView" : 32, + "componentType" : 5126, + "count" : 50, + "max" : [ + 0.7893224954605103, + 0.9619584083557129, + 1.0 + ], + "min" : [ + -0.9977105855941772, + -0.6139788627624512, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 33, + "componentType" : 5121, + "count" : 114, + "max" : [ + 77 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 34, + "componentType" : 5126, + "count" : 78, + "max" : [ + 0.7157971858978271, + 3.0, + 0.3306512236595154 + ], + "min" : [ + -0.7157971858978271, + -1.0, + -0.330651193857193 + ], + "type" : "VEC3" + }, + { + "bufferView" : 35, + "componentType" : 5126, + "count" : 78, + "max" : [ + 1.0, + 0.374779611825943, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 36, + "componentType" : 5121, + "count" : 78, + "max" : [ + 53 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 37, + "componentType" : 5126, + "count" : 54, + "max" : [ + 5.33065128326416, + 3.432579517364502, + -1.7226401567459106 + ], + "min" : [ + 4.66934871673584, + 2.4595587253570557, + -2.553251266479492 + ], + "type" : "VEC3" + }, + { + "bufferView" : 38, + "componentType" : 5126, + "count" : 54, + "max" : [ + 1.0, + 0.9992989301681519, + 0.9697774052619934 + ], + "min" : [ + -1.0, + -0.8278425335884094, + -0.9262295961380005 + ], + "type" : "VEC3" + } + ], + "asset" : { + "copyright" : "Copyright 2018 Analytical Graphics, Inc., CC-BY 4.0 https://creativecommons.org/licenses/by/4.0/ - Mesh and textures by Ed Mackey.", + "generator" : "Khronos Blender glTF 2.0 exporter, but with 3 quaternions manually converted to rotation matricies", + "version" : "2.0" + }, + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 0, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 624, + "byteOffset" : 80, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 624, + "byteOffset" : 704, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 1328, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 888, + "byteOffset" : 1444, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 888, + "byteOffset" : 2332, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 3220, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 3336, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 4272, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 5208, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 5324, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 6260, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 840, + "byteOffset" : 7196, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 3264, + "byteOffset" : 8036, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 3264, + "byteOffset" : 11300, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 14564, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 600, + "byteOffset" : 14644, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 600, + "byteOffset" : 15244, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 15844, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 15960, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 16896, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 17832, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 648, + "byteOffset" : 17912, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 648, + "byteOffset" : 18560, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 19208, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 19324, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 20260, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 21196, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 624, + "byteOffset" : 21276, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 624, + "byteOffset" : 21900, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 22524, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 600, + "byteOffset" : 22604, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 600, + "byteOffset" : 23204, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 23804, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 23920, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 24856, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 25792, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 648, + "byteOffset" : 25872, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 648, + "byteOffset" : 26520, + "target" : 34962 + } + ], + "buffers" : [ + { + "byteLength" : 27168, + "uri" : "data:application/octet-stream;base64,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AgADAAEAAAAEAAUABgAEAAcABQAIAAkACgALAAkACAAMAA0ADgAMAA8ADQAQABEAEgAQABMAEQAUABUAFgAUABcAFQAYABkAGgAbABwAHQAeAB8AIAAhACIAIwAkACUAJgAnACgAKQAqACsALAAtAC4ALwAwADEAMgAwADMAMQA0ADUANgA0ADcANQA4ADkAOgA4ADsAOQA8AD0APgA8AD8APQBAAEEAQgBAAEMAQQBEAEUARgBHAEUARABIAEkASgBIAEsASQBMAE0ATgBMAE8ATQBQAFEAUgBTAFEAUABUAFUAVgBUAFcAVQBYAFkAWgBYAFsAWQBcAF0AXgBfAF0AXABgAGEAYgBgAGMAYQBkAGUAZgBkAGcAZQBoAGkAagBoAGsAaQBsAG0AbgBsAG8AbQBwAHEAcgBwAHMAcQB0AHUAdgB0AHcAdQBgAHgAeQBgAGIAeAB6AHsAfAB6AH0AewB+AH8AgAB+AIEAfwCCAIMAhACFAIMAggCGAIcAiACGAIkAhwADAIoAiwADAAAAigCMAGMAjQBhAGMAjACOAI8AkACOAJEAjwCLAJIAkwCKAJIAiwCUAJUAlgCUAJcAlQCYAJkAmgCYAJsAmQCcAJ0AngCcAJ8AnQCgAKEAogCgAKMAoQCSAAEAkwACAAEAkgCkAKUApgCnAKUApAB4AI0AeQB4AIwAjQCoAKkAqgCrAKkAqACsAK0ArgCsAK8ArQCwALEAsgCwALMAsQC0ALUAtgC3ALUAtAC4ALkAugC4ALsAuQC8AL0AvgC8AL8AvQDAAMEAwgDAAMMAwQDEAMUAxgDEAMcAxQDIAMkAygDLAMkAyADMAM0AzgDMAM8AzQDQANEA0gDQANMA0QDUANUA1gDXANUA1ADYANkA2gDbANkA2ADcAN0A3gDcAN8A3QDgAOEA4gDgAOMA4QDkAOUA5gDnAOUA5ADoAOkA6gDoAOsA6QDsAO0A7gDsAO8A7QDwAPEA8gDwAPMA8QD0APUA9gD0APcA9QD4APkA+gD7APkA+AD8AP0A/gD/AP0A/AAAAQEBAgEAAQMBAQEEAQUBBgEEAQcBBQEIAQkBCgEIAQsBCQEMAQ0BDgEMAQ8BDQF7B4pAAACgwHwHikCT0JjAAACgwJDQmEB8B4rAAACgwHoHikCS0JhAAACgwJLQmEB6B4rAeweKQAAAoMAKzoFABs6BQAXilMAFzoHABs6BQArilMB/B4pAeweKQPv/n8D8/5/AeweKwIAHisD//5/AktCYQJLQmMD8/5/AktCYwJbQmMD//5/AeweKQHwHisCM0JhAktCYQAYAoEB/B4rAeweKQPz/n0B1B4pAeweKQAUAoECW0JjAktCYQPz/n0B6B4pAeweKwAAAoECT0JjAktCYwP7/n0CS0JhAktCYwAAAoEB8B4rAeweKwP7/n0AEAKDAktCYQI7QmEABAKDAeweKwHgHikABAKDAktCYwJDQmEAEAKDAeweKQHYHikAAAKBAktCYwJLQmMCS0JhAAACgwJLQmMCS0JhAktCYwAAAoMAAAKBAktCYwJLQmECS0JhAktCYwAAAoECS0JhAAACgwJLQmECT0JjAktCYwP7/n0ABAKDAktCYwJDQmECT0JjAAACgwJDQmECO0JjAktCYwAQAoMCO0JjAAACgwJbQmMD8/5/AktCYwJbQmMCX0JhAktCYQPv/n8CX0JhAAACgQI3QmMAFAKBAktCYQI3QmMCM0JhAktCYQAYAoED6/59AktCYQJjQmECM0JhAAACgQJjQmEAEAKDAktCYQI7QmECW0JjAktCYQPz/n0CW0JjAAACgQI7QmED//5/AktCYQJLQmMCR0JjAAACgQJLQmMCR0JjAktCYQAAAoMAAAKBAktCYwJLQmMCS0JhAAACgwJLQmECS0JhAAACgwJLQmMAAAKBAktCYwJLQmECO0JjAktCYwAQAoMCS0JhAAACgwJLQmMCO0JjAAACgwJbQmMCS0JhAktCYwAAAoMCX0JhAktCYQPv/n8AAAKBAktCYwJLQmMCS0JhAktCYwAAAoMAFAKBAktCYQI3QmMCS0JhAktCYwAAAoECT0JjAAACgwJDQmECS0JhAAACgwJLQmECT0JjAktCYwP7/n0D6/59AktCYQJjQmECS0JhAktCYwAAAoEAAAKBAktCYwJLQmECM0JhAktCYQAYAoECT0JjAAACgwJDQmED8/5/AktCYwJbQmMCO0JjAAACgwJbQmMABAKDAktCYwJDQmECW0JjAktCYQPz/n0ABAKDAktCYwJDQmECT0JjAktCYwP7/n0AEAKDAktCYQI7QmED//5/AktCYQJLQmMCO0JjAktCYwAQAoMD8/5/AktCYwJbQmMCR0JjAktCYQAAAoMAFAKBAktCYQI3QmMCM0JhAAACgQJjQmED6/59AktCYQJjQmECX0JhAAACgQI3QmMCR0JjAAACgQJLQmMCX0JhAktCYQPv/n8CR0JjAktCYQAAAoMCX0JhAAACgQI3QmMCM0JhAAACgQJjQmECW0JjAktCYQPz/n0CM0JhAktCYQAYAoECW0JjAAACgQI7QmEAEAKDAktCYQI7QmECR0JjAAACgQJLQmMD//5/AktCYQJLQmMCW0JjAAACgQI7QmECW0JjAAACgQI7QmEB1B4pAAACgQIAHikB+B4rAAACgQHYHikCM0JhAAACgQJjQmEAFAKBAeweKQHYHisAK4pRABs6BwAXOgcAAAKBAeweKwHoHisAP4pRABs6BQAHOgcAFAKBAeweKQHYHisAF4pRABs6BQAvOgUAP4pRABs6BQAHOgcD7/59AeweKQIAHikAFzoFABs6BwArilEB8B4rAeweKwP7/n0B6B4pAeweKwAAAoEAHzoHABs6BwAjilEB1B4pAAACgQIAHikAKzoFACuKUQAHOgcAAzoFACuKUQAvOgUB/B4pAAACgQHYHisAEAKDAeweKQHYHikAJ4pTABs6BQAbOgcAO4pTABs6BQALOgUD//5/AeweKQHwHisB6B4rAAACgQHwHisCR0JjAAACgQJLQmMAAAKBAeweKwHoHisAAAKBAktCYwJLQmEAAAKBAktCYwJLQmMAAAKBAeweKwHwHikCR0JjAktCYQAAAoMB3B4rAeweKwAQAoMCO0JjAktCYwAQAoMB6B4rAeweKQAAAoMAAAKBAeweKwHwHikD6/59AktCYQJjQmEAAAKBAktCYwJLQmED7/59AeweKQIAHikAEAKDAktCYQI7QmED//5/AeweKQHwHisAEAKDAeweKQHYHikD//5/AktCYQJLQmMB7B4pAAACgwHoHisCS0JhAAACgwJLQmMB/B4pAAACgQHYHisCX0JhAAACgQI3QmMCM0JhAktCYQAYAoEB6B4pAeweKwAAAoECS0JhAktCYwAAAoEB1B4pAeweKQAUAoEB3B4rAAACgwH4HisCO0JjAAACgwJbQmMAFAKBAktCYQI3QmMD7/59AeweKQIAHikAFAKBAeweKQHYHisD6/59AktCYQJjQmEABAKDAeweKwHgHikD8/5/AktCYwJbQmMABAKDAktCYwJDQmED8/5/AeweKwIAHisB3B4rAeweKwAQAoMCS0JhAktCYwAAAoMCO0JjAktCYwAQAoMB7B4pAeweKwAAAoMCR0JjAktCYQAAAoMB/B4pAeweKQPv/n8B6B4rAeweKQAAAoMCX0JhAktCYQPv/n8B7B4pAeweKwAAAoMCX0JhAktCYQPv/n8CS0JhAktCYwAAAoMB/B4pAeweKQPv/n8B8B4rAeweKwP7/n0CW0JjAktCYQPz/n0CT0JjAktCYwP7/n0B/B4rAeweKQPz/n0AFAKBAktCYQI3QmMAAAKBAeweKwHoHisAAAKBAktCYwJLQmMAFAKBAeweKQHYHisAFzoHABs6BQArilMAGzoFABs6BwArilMACzoHABs6BwA7ilMAKzoFABs6BQAXilMAGzoFACuKUwAbOgcAHzoHACuKUwATOgUACzoHACuKUwArOgcAGzoFACuKUwAbOgUAO4pTABs6BQALOgUAG4pTABs6BwArOgcAL4pTABs6BwATOgUAJ4pTABs6BQAbOgcAJzoHACuKUQAHOgUAKzoFACuKUQAHOgcAFzoHACuKUQAfOgcAAzoFACuKUQAvOgUAAzoFABs6BQA/ilEAHzoHABs6BwAjilEAFzoFABs6BwArilEAKzoHABs6BQAbilEAP4pRABs6BQAHOgcAK4pRABs6BwAfOgUAK4pRABs6BwAXOgcAF4pRABs6BQAvOgUAGzoFABs6BwArilMB/B4pAeweKQPv/n8B7B4pAeweKwAAAoMAKzoFABs6BQAXilMACzoHACuKUwArOgcB8B4rAAACgwHoHikB3B4rAAACgwH4HisAHzoHACuKUwATOgUB/B4pAAACgQHYHisAFzoHACuKUQAfOgcAKzoFACuKUQAHOgcB6B4rAAACgQHwHisB3B4rAeweKwAQAoMAGzoFABs6BwArilMB7B4pAeweKwAAAoMACzoHABs6BwA7ilMAHzoHABs6BwAjilEB/B4rAeweKQPz/n0B8B4rAeweKwP7/n0AKzoHABs6BQAbilEAL4pTABs6BwATOgUD8/5/AeweKwIAHisABAKDAeweKwHgHikAG4pTABs6BwArOgcB+B4rAAACgQHYHikAAzoFACuKUQAvOgUAJzoHACuKUQAHOgUB1B4pAAACgQIAHikB7B4pAAACgwHoHisACzoHACuKUwArOgcB3B4rA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spSqQO4jWUB2cgjATmuVQGKvW0BS1fu/TmuVQCXuS0DRIh/ATmuVQO1uQUB4aCPATmuVQMRCW0DcPQnATmuVQGKvW0BS1fu/TmuVQO1uQUB4aCPATmuVQGlpHUB6f9y/spSqQEQcQ0BQAyHAspSqQMwSWkDmNwDAspSqQK9WJUA4Zue/otqoQCXuS0DQIh/AspSqQO4jWUB2cgjAspSqQFh1SkBHah3AotqoQMRCW0DcPQnAotqoQMRCW0DcPQnAspSqQMwSWkDmNwDAspSqQO4jWUB2cgjAotqoQGKvW0BR1fu/otqoQCXuS0DQIh/AspSqQEQcQ0BQAyHAotqoQO5uQUB3aCPAspSqQFh1SkBHah3AotqoQO5uQUB3aCPAspSqQK9WJUA4Zue/otqoQGppHUB5f9y/spSqQEQcQ0BQAyHAspSqQK9WJUA4Zue/otqoQGKvW0BR1fu/otqoQGppHUB5f9y/spSqQMwSWkDmNwDATmuVQMRCW0DcPQnAotqoQCXuS0DQIh/ATmuVQCXuS0DRIh/AotqoQMRCW0DcPQnA/ihfMQ7Sfz/fWBm9/ihfMQ7Sfz/fWBm9/ihfMQ7Sfz/fWBm9/ihfMQ7Sfz/fWBm9Kb+bNOQAwT5iHW2/Kb+bNOQAwT5iHW2/Kb+bNOQAwT5iHW2/Kb+bNOQAwT5iHW2/b0gDNX3tU78fmw+/b0gDNX3tU78fmw+/b0gDNX3tU78fmw+/b0gDNX3tU78fmw+/AnB5tNjYeT5VQ3g/AnB5tNjYeT5VQ3g/AnB5tNjYeT5VQ3g/AnB5tNjYeT5VQ3g/AACAPwAAAIAAAAAAAACAPwAAAIAAAAAAAACAPwAAAIAAAAAAAACAPwAAAIAAAAAA//9/vwAAAAAAAACA//9/vwAAAAAAAACA//9/vwAAAAAAAACA//9/vwAAAAAAAACAAACAvwAAAAAAAACAAACAvwAAAAAAAACAAACAvwAAAAAAAACA//9/PwAAAAAAAAAA//9/PwAAAAAAAAAA//9/PwAAAAAAAAAAIzcJPw4JMT827Pe+IzcJPw4JMT827Pe+IzcJPw4JMT827Pe+IzcJPw4JMT827Pe+uJvtPgg4Yj8jQXu9uJvtPgg4Yj8jQXu9uJvtPgg4Yj8jQXu9uJvtPgg4Yj8jQXu9P+jwPk5Ntj4zr06/P+jwPk5Ntj4zr06/P+jwPk5Ntj4zr06/P+jwPk5Ntj4zr06/v1UsP59nHb89VtK+v1UsP59nHb89VtK+v1UsP59nHb89VtK+v1UsP59nHb89VtK+1EwoP8KrNz5iWzs/1EwoP8KrNz5iWzs/1EwoP8KrNz5iWzs/1EwoP8KrNz5iWzs/4sjcM/+zUT/W1RK/4sjcM/+zUT/W1RK/4sjcM/+zUT/W1RK/4sjcM/+zUT/W1RK/" + } + ], + "materials" : [ + { + "name" : "BaseMaterial", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.3401939868927002, + 0.6781029105186462, + 1.0, + 1.0 + ], + "roughnessFactor" : 0.5 + } + }, + { + "name" : "MatX1", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.800000011920929, + 0.0, + 0.0, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + }, + { + "name" : "MatX2", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.0, + 0.800000011920929, + 0.800000011920929, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + }, + { + "name" : "MatY1", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.0, + 0.800000011920929, + 0.0, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + }, + { + "name" : "MatY2", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.800000011920929, + 0.0, + 0.800000011920929, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + }, + { + "name" : "MatZ1", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.0, + 0.0, + 0.800000011920929, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + }, + { + "name" : "MatZ2", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.800000011920929, + 0.800000011920929, + 0.0, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + } + ], + "meshes" : [ + { + "name" : "TargetMeshZ1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 2, + "POSITION" : 1 + }, + "indices" : 0, + "material" : 5 + } + ] + }, + { + "name" : "ArrowMeshZ1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 5, + "POSITION" : 4 + }, + "indices" : 3, + "material" : 5 + } + ] + }, + { + "name" : "ArrowMeshY2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 8, + "POSITION" : 7 + }, + "indices" : 6, + "material" : 4 + } + ] + }, + { + "name" : "ArrowMeshY1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 11, + "POSITION" : 10 + }, + "indices" : 9, + "material" : 3 + } + ] + }, + { + "name" : "BaseCubeMesh", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 14, + "POSITION" : 13 + }, + "indices" : 12, + "material" : 0 + } + ] + }, + { + "name" : "TargetMeshY2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 17, + "POSITION" : 16 + }, + "indices" : 15, + "material" : 4 + } + ] + }, + { + "name" : "ArrowMeshX1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 20, + "POSITION" : 19 + }, + "indices" : 18, + "material" : 1 + } + ] + }, + { + "name" : "TargetMeshX2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 23, + "POSITION" : 22 + }, + "indices" : 21, + "material" : 2 + } + ] + }, + { + "name" : "ArrowMeshX2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 26, + "POSITION" : 25 + }, + "indices" : 24, + "material" : 2 + } + ] + }, + { + "name" : "TargetMeshY1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 29, + "POSITION" : 28 + }, + "indices" : 27, + "material" : 3 + } + ] + }, + { + "name" : "TargetMeshZ2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 32, + "POSITION" : 31 + }, + "indices" : 30, + "material" : 6 + } + ] + }, + { + "name" : "ArrowMeshZ2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 35, + "POSITION" : 34 + }, + "indices" : 33, + "material" : 6 + } + ] + }, + { + "name" : "TargetMeshX1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 38, + "POSITION" : 37 + }, + "indices" : 36, + "material" : 1 + } + ] + } + ], + "nodes" : [ + { + "mesh" : 6, + "name" : "ArrowX1", + "rotation" : [ + -0.30070576071739197, + 0.0, + -0.0, + 0.9537169933319092 + ], + "scale" : [ + 1.0, + 0.9999999403953552, + 0.9999999403953552 + ], + "translation" : [ + 5.0, + 0.0, + -0.0 + ] + }, + { + "mesh" : 8, + "name" : "ArrowX2", + "matrix": [ + 1.0000000221841605, + 0, + 0, + 0, + 0, + 0.9961947216654676, + 0.08715572783347625, + 0, + 0, + -0.08715572783347625, + 0.9961947216654676, + 0, + -5.0, + 0, + 0, + 1 + ] + }, + { + "mesh" : 3, + "name" : "ArrowY1", + "rotation" : [ + 0.0, + -0.5735764503479004, + -0.0, + 0.8191520571708679 + ], + "translation" : [ + 0.0, + 5.0, + -0.0 + ] + }, + { + "mesh" : 2, + "name" : "ArrowY2", + "matrix": [ + 0.9781476413655263, + 0, + 0.20791169731909154, + 0, + 0, + 1.000000041095523, + 0, + 0, + -0.20791169731909154, + 0, + 0.9781476413655263, + 0, + 0, + -5, + 0, + 1 + ] + }, + { + "mesh" : 1, + "name" : "ArrowZ1", + "rotation" : [ + 0.0, + 0.0, + 0.13052621483802795, + 0.9914449453353882 + ], + "scale" : [ + 0.9999999403953552, + 0.9999999403953552, + 1.0 + ], + "translation" : [ + 0.0, + 0.0, + 5.0 + ] + }, + { + "mesh" : 11, + "name" : "ArrowZ2", + "matrix": [ + 0.9563047863767122, + -0.2923716890963366, + 0, + 0, + 0.2923716890963366, + 0.9563047863767122, + 0, + 0, + 0, + 0, + 1.0000000245160268, + 0, + 0, + 0, + -5, + 1 + ] + }, + { + "mesh" : 4, + "name" : "BaseCube" + }, + { + "mesh" : 12, + "name" : "TargetX1" + }, + { + "mesh" : 7, + "name" : "TargetX2" + }, + { + "mesh" : 9, + "name" : "TargetY1" + }, + { + "mesh" : 5, + "name" : "TargetY2" + }, + { + "mesh" : 0, + "name" : "TargetZ1" + }, + { + "mesh" : 10, + "name" : "TargetZ2" + } + ], + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 5, + 12, + 10, + 3, + 1, + 8, + 11, + 4, + 7, + 0, + 9, + 2, + 6 + ] + } + ] +} diff --git a/test/testfiles/gltf/OrientationTest/glTF/OrientationTest.bin b/test/testfiles/gltf/OrientationTest/glTF/OrientationTest.bin new file mode 100644 index 0000000..efeb712 Binary files /dev/null and b/test/testfiles/gltf/OrientationTest/glTF/OrientationTest.bin differ diff --git a/test/testfiles/gltf/OrientationTest/glTF/OrientationTest.gltf b/test/testfiles/gltf/OrientationTest/glTF/OrientationTest.gltf new file mode 100644 index 0000000..29fb9e9 --- /dev/null +++ b/test/testfiles/gltf/OrientationTest/glTF/OrientationTest.gltf @@ -0,0 +1,1258 @@ +{ + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5121, + "count" : 78, + "max" : [ + 51 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 52, + "max" : [ + -0.6740907430648804, + 3.9529545307159424, + 5.33065128326416 + ], + "min" : [ + -1.3648574352264404, + 2.9005930423736572, + 4.66934871673584 + ], + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 52, + "max" : [ + 0.994742751121521, + 0.9659255146980286, + 1.0 + ], + "min" : [ + -0.8102691173553467, + -0.5860580801963806, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 3, + "componentType" : 5121, + "count" : 114, + "max" : [ + 73 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 74, + "max" : [ + 0.7157971262931824, + 3.0, + 0.33065110445022583 + ], + "min" : [ + -0.7157971262931824, + -1.0, + -0.33065110445022583 + ], + "type" : "VEC3" + }, + { + "bufferView" : 5, + "componentType" : 5126, + "count" : 74, + "max" : [ + 1.0, + 0.374779611825943, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 6, + "componentType" : 5121, + "count" : 114, + "max" : [ + 77 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 7, + "componentType" : 5126, + "count" : 78, + "max" : [ + 0.7157971262931824, + 0.33065134286880493, + 3.0 + ], + "min" : [ + -0.7157971262931824, + -0.3306511640548706, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 8, + "componentType" : 5126, + "count" : 78, + "max" : [ + 1.0, + 1.0, + 0.374779611825943 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 9, + "componentType" : 5121, + "count" : 114, + "max" : [ + 77 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 10, + "componentType" : 5126, + "count" : 78, + "max" : [ + 0.7157971262931824, + 0.33065131306648254, + 1.0 + ], + "min" : [ + -0.7157971262931824, + -0.330651193857193, + -3.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 11, + "componentType" : 5126, + "count" : 78, + "max" : [ + 1.0, + 1.0, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -0.374779611825943 + ], + "type" : "VEC3" + }, + { + "bufferView" : 12, + "componentType" : 5123, + "count" : 420, + "max" : [ + 271 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 13, + "componentType" : 5126, + "count" : 272, + "max" : [ + 5.000002384185791, + 5.0, + 5.000002861022949 + ], + "min" : [ + -5.000001907348633, + -5.0, + -5.000001907348633 + ], + "type" : "VEC3" + }, + { + "bufferView" : 14, + "componentType" : 5126, + "count" : 272, + "max" : [ + 1.0, + 1.0, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 15, + "componentType" : 5121, + "count" : 78, + "max" : [ + 49 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 16, + "componentType" : 5126, + "count" : 50, + "max" : [ + -0.46912679076194763, + -4.66934871673584, + 3.9916374683380127 + ], + "min" : [ + -1.1686336994171143, + -5.330650806427002, + 2.93727445602417 + ], + "type" : "VEC3" + }, + { + "bufferView" : 17, + "componentType" : 5126, + "count" : 50, + "max" : [ + 0.9880198836326599, + 1.0, + 0.9781473875045776 + ], + "min" : [ + -0.8398305773735046, + -1.0, + -0.542848527431488 + ], + "type" : "VEC3" + }, + { + "bufferView" : 18, + "componentType" : 5121, + "count" : 114, + "max" : [ + 77 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 19, + "componentType" : 5126, + "count" : 78, + "max" : [ + 0.3306511640548706, + 3.0, + 0.7157971262931824 + ], + "min" : [ + -0.3306511640548706, + -1.0, + -0.7157971262931824 + ], + "type" : "VEC3" + }, + { + "bufferView" : 20, + "componentType" : 5126, + "count" : 78, + "max" : [ + 1.0, + 0.374779611825943, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 21, + "componentType" : 5121, + "count" : 78, + "max" : [ + 53 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 22, + "componentType" : 5126, + "count" : 54, + "max" : [ + -4.66934871673584, + 4.039160251617432, + 0.6999828815460205 + ], + "min" : [ + -5.33065128326416, + 2.991360902786255, + -0.012430161237716675 + ], + "type" : "VEC3" + }, + { + "bufferView" : 23, + "componentType" : 5126, + "count" : 54, + "max" : [ + 1.0, + 0.9961948394775391, + 0.8997273445129395 + ], + "min" : [ + -1.0, + -0.4364525079727173, + -0.9618476033210754 + ], + "type" : "VEC3" + }, + { + "bufferView" : 24, + "componentType" : 5121, + "count" : 114, + "max" : [ + 77 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 25, + "componentType" : 5126, + "count" : 78, + "max" : [ + 0.3306511640548706, + 3.0, + 0.7157971262931824 + ], + "min" : [ + -0.3306511640548706, + -1.0, + -0.7157971262931824 + ], + "type" : "VEC3" + }, + { + "bufferView" : 26, + "componentType" : 5126, + "count" : 78, + "max" : [ + 1.0, + 0.374779611825943, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 27, + "componentType" : 5121, + "count" : 78, + "max" : [ + 51 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 28, + "componentType" : 5126, + "count" : 52, + "max" : [ + 3.864471435546875, + 5.33065128326416, + -1.0113166570663452 + ], + "min" : [ + 2.8218495845794678, + 4.669349193572998, + -1.6833229064941406 + ], + "type" : "VEC3" + }, + { + "bufferView" : 29, + "componentType" : 5126, + "count" : 52, + "max" : [ + 0.9749377965927124, + 1.0, + 0.9998826384544373 + ], + "min" : [ + -0.654447615146637, + -1.0, + -0.7971041202545166 + ], + "type" : "VEC3" + }, + { + "bufferView" : 30, + "componentType" : 5121, + "count" : 78, + "max" : [ + 49 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 31, + "componentType" : 5126, + "count" : 50, + "max" : [ + 1.4936277866363525, + 3.9211390018463135, + -4.66934871673584 + ], + "min" : [ + 0.8097413778305054, + 2.8717148303985596, + -5.33065128326416 + ], + "type" : "VEC3" + }, + { + "bufferView" : 32, + "componentType" : 5126, + "count" : 50, + "max" : [ + 0.7893224954605103, + 0.9619584083557129, + 1.0 + ], + "min" : [ + -0.9977105855941772, + -0.6139788627624512, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 33, + "componentType" : 5121, + "count" : 114, + "max" : [ + 77 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 34, + "componentType" : 5126, + "count" : 78, + "max" : [ + 0.7157971858978271, + 3.0, + 0.3306512236595154 + ], + "min" : [ + -0.7157971858978271, + -1.0, + -0.330651193857193 + ], + "type" : "VEC3" + }, + { + "bufferView" : 35, + "componentType" : 5126, + "count" : 78, + "max" : [ + 1.0, + 0.374779611825943, + 1.0 + ], + "min" : [ + -1.0, + -1.0, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 36, + "componentType" : 5121, + "count" : 78, + "max" : [ + 53 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 37, + "componentType" : 5126, + "count" : 54, + "max" : [ + 5.33065128326416, + 3.432579517364502, + -1.7226401567459106 + ], + "min" : [ + 4.66934871673584, + 2.4595587253570557, + -2.553251266479492 + ], + "type" : "VEC3" + }, + { + "bufferView" : 38, + "componentType" : 5126, + "count" : 54, + "max" : [ + 1.0, + 0.9992989301681519, + 0.9697774052619934 + ], + "min" : [ + -1.0, + -0.8278425335884094, + -0.9262295961380005 + ], + "type" : "VEC3" + } + ], + "asset" : { + "copyright" : "Copyright 2018 Analytical Graphics, Inc., CC-BY 4.0 https://creativecommons.org/licenses/by/4.0/ - Mesh and textures by Ed Mackey.", + "generator" : "Khronos Blender glTF 2.0 exporter, but with 3 quaternions manually converted to rotation matricies", + "version" : "2.0" + }, + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 0, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 624, + "byteOffset" : 80, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 624, + "byteOffset" : 704, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 1328, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 888, + "byteOffset" : 1444, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 888, + "byteOffset" : 2332, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 3220, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 3336, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 4272, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 5208, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 5324, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 6260, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 840, + "byteOffset" : 7196, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 3264, + "byteOffset" : 8036, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 3264, + "byteOffset" : 11300, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 14564, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 600, + "byteOffset" : 14644, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 600, + "byteOffset" : 15244, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 15844, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 15960, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 16896, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 17832, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 648, + "byteOffset" : 17912, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 648, + "byteOffset" : 18560, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 19208, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 19324, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 20260, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 21196, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 624, + "byteOffset" : 21276, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 624, + "byteOffset" : 21900, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 22524, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 600, + "byteOffset" : 22604, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 600, + "byteOffset" : 23204, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 114, + "byteOffset" : 23804, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 23920, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 936, + "byteOffset" : 24856, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 78, + "byteOffset" : 25792, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 648, + "byteOffset" : 25872, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 648, + "byteOffset" : 26520, + "target" : 34962 + } + ], + "buffers" : [ + { + "byteLength" : 27168, + "uri" : "OrientationTest.bin" + } + ], + "materials" : [ + { + "name" : "BaseMaterial", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.3401939868927002, + 0.6781029105186462, + 1.0, + 1.0 + ], + "roughnessFactor" : 0.5 + } + }, + { + "name" : "MatX1", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.800000011920929, + 0.0, + 0.0, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + }, + { + "name" : "MatX2", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.0, + 0.800000011920929, + 0.800000011920929, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + }, + { + "name" : "MatY1", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.0, + 0.800000011920929, + 0.0, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + }, + { + "name" : "MatY2", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.800000011920929, + 0.0, + 0.800000011920929, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + }, + { + "name" : "MatZ1", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.0, + 0.0, + 0.800000011920929, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + }, + { + "name" : "MatZ2", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.800000011920929, + 0.800000011920929, + 0.0, + 1.0 + ], + "metallicFactor" : 0.4000000059604645, + "roughnessFactor" : 0.20000000298023224 + } + } + ], + "meshes" : [ + { + "name" : "TargetMeshZ1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 2, + "POSITION" : 1 + }, + "indices" : 0, + "material" : 5 + } + ] + }, + { + "name" : "ArrowMeshZ1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 5, + "POSITION" : 4 + }, + "indices" : 3, + "material" : 5 + } + ] + }, + { + "name" : "ArrowMeshY2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 8, + "POSITION" : 7 + }, + "indices" : 6, + "material" : 4 + } + ] + }, + { + "name" : "ArrowMeshY1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 11, + "POSITION" : 10 + }, + "indices" : 9, + "material" : 3 + } + ] + }, + { + "name" : "BaseCubeMesh", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 14, + "POSITION" : 13 + }, + "indices" : 12, + "material" : 0 + } + ] + }, + { + "name" : "TargetMeshY2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 17, + "POSITION" : 16 + }, + "indices" : 15, + "material" : 4 + } + ] + }, + { + "name" : "ArrowMeshX1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 20, + "POSITION" : 19 + }, + "indices" : 18, + "material" : 1 + } + ] + }, + { + "name" : "TargetMeshX2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 23, + "POSITION" : 22 + }, + "indices" : 21, + "material" : 2 + } + ] + }, + { + "name" : "ArrowMeshX2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 26, + "POSITION" : 25 + }, + "indices" : 24, + "material" : 2 + } + ] + }, + { + "name" : "TargetMeshY1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 29, + "POSITION" : 28 + }, + "indices" : 27, + "material" : 3 + } + ] + }, + { + "name" : "TargetMeshZ2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 32, + "POSITION" : 31 + }, + "indices" : 30, + "material" : 6 + } + ] + }, + { + "name" : "ArrowMeshZ2", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 35, + "POSITION" : 34 + }, + "indices" : 33, + "material" : 6 + } + ] + }, + { + "name" : "TargetMeshX1", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 38, + "POSITION" : 37 + }, + "indices" : 36, + "material" : 1 + } + ] + } + ], + "nodes" : [ + { + "mesh" : 6, + "name" : "ArrowX1", + "rotation" : [ + -0.30070576071739197, + 0.0, + -0.0, + 0.9537169933319092 + ], + "scale" : [ + 1.0, + 0.9999999403953552, + 0.9999999403953552 + ], + "translation" : [ + 5.0, + 0.0, + -0.0 + ] + }, + { + "mesh" : 8, + "name" : "ArrowX2", + "matrix": [ + 1.0000000221841605, + 0, + 0, + 0, + 0, + 0.9961947216654676, + 0.08715572783347625, + 0, + 0, + -0.08715572783347625, + 0.9961947216654676, + 0, + -5.0, + 0, + 0, + 1 + ] + }, + { + "mesh" : 3, + "name" : "ArrowY1", + "rotation" : [ + 0.0, + -0.5735764503479004, + -0.0, + 0.8191520571708679 + ], + "translation" : [ + 0.0, + 5.0, + -0.0 + ] + }, + { + "mesh" : 2, + "name" : "ArrowY2", + "matrix": [ + 0.9781476413655263, + 0, + 0.20791169731909154, + 0, + 0, + 1.000000041095523, + 0, + 0, + -0.20791169731909154, + 0, + 0.9781476413655263, + 0, + 0, + -5, + 0, + 1 + ] + }, + { + "mesh" : 1, + "name" : "ArrowZ1", + "rotation" : [ + 0.0, + 0.0, + 0.13052621483802795, + 0.9914449453353882 + ], + "scale" : [ + 0.9999999403953552, + 0.9999999403953552, + 1.0 + ], + "translation" : [ + 0.0, + 0.0, + 5.0 + ] + }, + { + "mesh" : 11, + "name" : "ArrowZ2", + "matrix": [ + 0.9563047863767122, + -0.2923716890963366, + 0, + 0, + 0.2923716890963366, + 0.9563047863767122, + 0, + 0, + 0, + 0, + 1.0000000245160268, + 0, + 0, + 0, + -5, + 1 + ] + }, + { + "mesh" : 4, + "name" : "BaseCube" + }, + { + "mesh" : 12, + "name" : "TargetX1" + }, + { + "mesh" : 7, + "name" : "TargetX2" + }, + { + "mesh" : 9, + "name" : "TargetY1" + }, + { + "mesh" : 5, + "name" : "TargetY2" + }, + { + "mesh" : 0, + "name" : "TargetZ1" + }, + { + "mesh" : 10, + "name" : "TargetZ2" + } + ], + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 5, + 12, + 10, + 3, + 1, + 8, + 11, + 4, + 7, + 0, + 9, + 2, + 6 + ] + } + ] +} diff --git a/test/testfiles/gltf/OrientationTest/screenshot/OrientationTestFail.png b/test/testfiles/gltf/OrientationTest/screenshot/OrientationTestFail.png new file mode 100644 index 0000000..14c3de2 Binary files /dev/null and b/test/testfiles/gltf/OrientationTest/screenshot/OrientationTestFail.png differ diff --git a/test/testfiles/gltf/OrientationTest/screenshot/screenshot.png b/test/testfiles/gltf/OrientationTest/screenshot/screenshot.png new file mode 100644 index 0000000..a90b620 Binary files /dev/null and b/test/testfiles/gltf/OrientationTest/screenshot/screenshot.png differ diff --git a/test/testfiles/gltf/SimpleMeshes/README.md b/test/testfiles/gltf/SimpleMeshes/README.md new file mode 100644 index 0000000..e74490b --- /dev/null +++ b/test/testfiles/gltf/SimpleMeshes/README.md @@ -0,0 +1,25 @@ +# SimpleMeshes + +This is an example showing how the same mesh may be appended to +several nodes, to be rendered multiple times. It also shows +how additional vertex attributes are defined in a `mesh.primitive` - +namely, attributes for the vertex normals. + +**Note:** The additional vertex normal attribute in this example is +not yet used by any technique. This may cause a warning to be +printed during the validation. The normal attribute will be used in +the [AdvancedMaterial](../AdvancedMaterial) example. + +## Screenshot + +![screenshot](screenshot/screenshot.png) + +## License Information + +Public domain ([CC0](https://creativecommons.org/publicdomain/zero/1.0/)) + +## Data layout + +The following image shows the data layout of this sample: + +![triangle](screenshot/triangle.png) diff --git a/test/testfiles/gltf/SimpleMeshes/glTF-Embedded/SimpleMeshes.gltf b/test/testfiles/gltf/SimpleMeshes/glTF-Embedded/SimpleMeshes.gltf new file mode 100644 index 0000000..cedd751 --- /dev/null +++ b/test/testfiles/gltf/SimpleMeshes/glTF-Embedded/SimpleMeshes.gltf @@ -0,0 +1,85 @@ +{ + "scene" : 0, + "scenes" : [ + { + "nodes" : [ 0, 1] + } + ], + "nodes" : [ + { + "mesh" : 0 + }, + { + "mesh" : 0, + "translation" : [ 1.0, 0.0, 0.0 ] + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1, + "NORMAL" : 2 + }, + "indices" : 0 + } ] + } + ], + + "buffers" : [ + { + "uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8=", + "byteLength" : 80 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 6, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 8, + "byteLength" : 72, + "byteStride" : 12, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 3, + "type" : "SCALAR", + "max" : [ 2 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 36, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 0.0, 0.0, 1.0 ], + "min" : [ 0.0, 0.0, 1.0 ] + } + ], + + "asset" : { + "version" : "2.0" + } +} + diff --git a/test/testfiles/gltf/SimpleMeshes/glTF/SimpleMeshes.gltf b/test/testfiles/gltf/SimpleMeshes/glTF/SimpleMeshes.gltf new file mode 100644 index 0000000..0737634 --- /dev/null +++ b/test/testfiles/gltf/SimpleMeshes/glTF/SimpleMeshes.gltf @@ -0,0 +1,84 @@ +{ + "scene" : 0, + "scenes" : [ + { + "nodes" : [ 0, 1] + } + ], + "nodes" : [ + { + "mesh" : 0 + }, + { + "mesh" : 0, + "translation" : [ 1.0, 0.0, 0.0 ] + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1, + "NORMAL" : 2 + }, + "indices" : 0 + } ] + } + ], + + "buffers" : [ + { + "uri" : "triangle.bin", + "byteLength" : 80 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 6, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 8, + "byteLength" : 72, + "byteStride" : 12, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 3, + "type" : "SCALAR", + "max" : [ 2 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 36, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 0.0, 0.0, 1.0 ], + "min" : [ 0.0, 0.0, 1.0 ] + } + ], + + "asset" : { + "version" : "2.0" + } +} diff --git a/test/testfiles/gltf/SimpleMeshes/glTF/triangle.bin b/test/testfiles/gltf/SimpleMeshes/glTF/triangle.bin new file mode 100644 index 0000000..291b43c Binary files /dev/null and b/test/testfiles/gltf/SimpleMeshes/glTF/triangle.bin differ diff --git a/test/testfiles/gltf/SimpleMeshes/screenshot/screenshot.png b/test/testfiles/gltf/SimpleMeshes/screenshot/screenshot.png new file mode 100644 index 0000000..650bf80 Binary files /dev/null and b/test/testfiles/gltf/SimpleMeshes/screenshot/screenshot.png differ diff --git a/test/testfiles/gltf/SimpleMeshes/screenshot/triangle.png b/test/testfiles/gltf/SimpleMeshes/screenshot/triangle.png new file mode 100644 index 0000000..b4c5829 Binary files /dev/null and b/test/testfiles/gltf/SimpleMeshes/screenshot/triangle.png differ diff --git a/test/testfiles/gltf/SimpleSparseAccessor/README.md b/test/testfiles/gltf/SimpleSparseAccessor/README.md new file mode 100644 index 0000000..ed75c40 --- /dev/null +++ b/test/testfiles/gltf/SimpleSparseAccessor/README.md @@ -0,0 +1,22 @@ +# SimpleSparseAccessor + +## Screenshot + +![screenshot](screenshot/screenshot.png) + +## Data layout + +The following image shows the data layout of this sample: + +![simpleSparseAccessorStructure](screenshot/simpleSparseAccessorStructure.png) + +## Geometry + +The following image shows the substitution of the values that takes place for the sparse accessor: + +![SimpleSparseAccessorDescription](screenshot/SimpleSparseAccessorDescription.png) + +## License Information + +Public domain ([CC0](https://creativecommons.org/publicdomain/zero/1.0/)) + diff --git a/test/testfiles/gltf/SimpleSparseAccessor/glTF-Embedded/SimpleSparseAccessor.gltf b/test/testfiles/gltf/SimpleSparseAccessor/glTF-Embedded/SimpleSparseAccessor.gltf new file mode 100644 index 0000000..ca69e2f --- /dev/null +++ b/test/testfiles/gltf/SimpleSparseAccessor/glTF-Embedded/SimpleSparseAccessor.gltf @@ -0,0 +1,77 @@ +{ + "scene" : 0, + "scenes" : [ { + "nodes" : [ 0 ] + } ], + + "nodes" : [ { + "mesh" : 0 + } ], + + "meshes" : [ { + "primitives" : [ { + "attributes" : { + "POSITION" : 1 + }, + "indices" : 0 + } ] + } ], + + "buffers" : [ { + "uri" : "data:application/gltf-buffer;base64,AAAIAAcAAAABAAgAAQAJAAgAAQACAAkAAgAKAAkAAgADAAoAAwALAAoAAwAEAAsABAAMAAsABAAFAAwABQANAAwABQAGAA0AAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAQAAAAAAAAAAAAABAQAAAAAAAAAAAAACAQAAAAAAAAAAAAACgQAAAAAAAAAAAAADAQAAAAAAAAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAQAAAgD8AAAAAAABAQAAAgD8AAAAAAACAQAAAgD8AAAAAAACgQAAAgD8AAAAAAADAQAAAgD8AAAAACAAKAAwAAAAAAIA/AAAAQAAAAAAAAEBAAABAQAAAAAAAAKBAAACAQAAAAAA=", + "byteLength" : 284 + } ], + + "bufferViews" : [ { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 72, + "target" : 34963 + }, { + "buffer" : 0, + "byteOffset" : 72, + "byteLength" : 168 + }, { + "buffer" : 0, + "byteOffset" : 240, + "byteLength" : 6 + }, { + "buffer" : 0, + "byteOffset" : 248, + "byteLength" : 36 + } ], + + "accessors" : [ { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 36, + "type" : "SCALAR", + "max" : [ 13 ], + "min" : [ 0 ] + }, { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 14, + "type" : "VEC3", + "max" : [ 6.0, 4.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ], + "sparse" : { + "count" : 3, + "indices" : { + "bufferView" : 2, + "byteOffset" : 0, + "componentType" : 5123 + }, + "values" : { + "bufferView" : 3, + "byteOffset" : 0 + } + } + } ], + + "asset" : { + "version" : "2.0" + } +} \ No newline at end of file diff --git a/test/testfiles/gltf/SimpleSparseAccessor/glTF/SimpleSparseAccessor.bin b/test/testfiles/gltf/SimpleSparseAccessor/glTF/SimpleSparseAccessor.bin new file mode 100644 index 0000000..99eaf33 Binary files /dev/null and b/test/testfiles/gltf/SimpleSparseAccessor/glTF/SimpleSparseAccessor.bin differ diff --git a/test/testfiles/gltf/SimpleSparseAccessor/glTF/SimpleSparseAccessor.gltf b/test/testfiles/gltf/SimpleSparseAccessor/glTF/SimpleSparseAccessor.gltf new file mode 100644 index 0000000..b0c003b --- /dev/null +++ b/test/testfiles/gltf/SimpleSparseAccessor/glTF/SimpleSparseAccessor.gltf @@ -0,0 +1,106 @@ +{ + "scene" : 0, + "scenes":[ + { + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0 + } + ], + "meshes":[ + { + "primitives":[ + { + "attributes":{ + "POSITION":1 + }, + "indices":0 + } + ] + } + ], + "buffers":[ + { + "uri":"SimpleSparseAccessor.bin", + "byteLength":284 + } + ], + "bufferViews":[ + { + "buffer":0, + "byteOffset":0, + "byteLength":72, + "target":34963 + }, + { + "buffer":0, + "byteOffset":72, + "byteLength":168 + }, + { + "buffer":0, + "byteOffset":240, + "byteLength":6 + }, + { + "buffer":0, + "byteOffset":248, + "byteLength":36 + } + ], + "accessors":[ + { + "bufferView":0, + "byteOffset":0, + "componentType":5123, + "count":36, + "type":"SCALAR", + "max":[ + 13 + ], + "min":[ + 0 + ] + }, + { + "bufferView":1, + "byteOffset":0, + "componentType":5126, + "count":14, + "type":"VEC3", + "sparse":{ + "count":3, + "indices":{ + "bufferView":2, + "byteOffset":0, + "componentType":5123 + }, + "values":{ + "bufferView":3, + "byteOffset":0 + } + }, + "max":[ + 6.0, + 4.0, + 0.0 + ], + "min":[ + 0.0, + 0.0, + 0.0 + ] + } + ], + "asset":{ + "version":"2.0" + } +} + + + diff --git a/test/testfiles/gltf/SimpleSparseAccessor/screenshot/SimpleSparseAccessorDescription.png b/test/testfiles/gltf/SimpleSparseAccessor/screenshot/SimpleSparseAccessorDescription.png new file mode 100644 index 0000000..ff53acf Binary files /dev/null and b/test/testfiles/gltf/SimpleSparseAccessor/screenshot/SimpleSparseAccessorDescription.png differ diff --git a/test/testfiles/gltf/SimpleSparseAccessor/screenshot/screenshot.png b/test/testfiles/gltf/SimpleSparseAccessor/screenshot/screenshot.png new file mode 100644 index 0000000..76cbc3e Binary files /dev/null and b/test/testfiles/gltf/SimpleSparseAccessor/screenshot/screenshot.png differ diff --git a/test/testfiles/gltf/SimpleSparseAccessor/screenshot/simpleSparseAccessorStructure.png b/test/testfiles/gltf/SimpleSparseAccessor/screenshot/simpleSparseAccessorStructure.png new file mode 100644 index 0000000..e44ac0d Binary files /dev/null and b/test/testfiles/gltf/SimpleSparseAccessor/screenshot/simpleSparseAccessorStructure.png differ diff --git a/test/testfiles/gltf/Triangle/README.md b/test/testfiles/gltf/Triangle/README.md new file mode 100644 index 0000000..9f237d6 --- /dev/null +++ b/test/testfiles/gltf/Triangle/README.md @@ -0,0 +1,15 @@ +# Triangle + +## Screenshot + +![screenshot](screenshot/screenshot.png) + +## License Information + +Public domain ([CC0](https://creativecommons.org/publicdomain/zero/1.0/)) + +## Data layout + +The following image shows the data layout of this sample: + +![simpleTriangle](screenshot/simpleTriangle.png) diff --git a/test/testfiles/gltf/Triangle/glTF-Embedded/Triangle.gltf b/test/testfiles/gltf/Triangle/glTF-Embedded/Triangle.gltf new file mode 100644 index 0000000..40cab5a --- /dev/null +++ b/test/testfiles/gltf/Triangle/glTF-Embedded/Triangle.gltf @@ -0,0 +1,70 @@ +{ + "scene" : 0, + "scenes" : [ + { + "nodes" : [ 0 ] + } + ], + + "nodes" : [ + { + "mesh" : 0 + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1 + }, + "indices" : 0 + } ] + } + ], + + "buffers" : [ + { + "uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=", + "byteLength" : 44 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 6, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 8, + "byteLength" : 36, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 3, + "type" : "SCALAR", + "max" : [ 2 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + } + ], + + "asset" : { + "version" : "2.0" + } +} diff --git a/test/testfiles/gltf/Triangle/glTF/Triangle.gltf b/test/testfiles/gltf/Triangle/glTF/Triangle.gltf new file mode 100644 index 0000000..0faabea --- /dev/null +++ b/test/testfiles/gltf/Triangle/glTF/Triangle.gltf @@ -0,0 +1,70 @@ +{ + "scene" : 0, + "scenes" : [ + { + "nodes" : [ 0 ] + } + ], + + "nodes" : [ + { + "mesh" : 0 + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1 + }, + "indices" : 0 + } ] + } + ], + + "buffers" : [ + { + "uri" : "simpleTriangle.bin", + "byteLength" : 44 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 6, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 8, + "byteLength" : 36, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 3, + "type" : "SCALAR", + "max" : [ 2 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + } + ], + + "asset" : { + "version" : "2.0" + } +} diff --git a/test/testfiles/gltf/Triangle/glTF/simpleTriangle.bin b/test/testfiles/gltf/Triangle/glTF/simpleTriangle.bin new file mode 100644 index 0000000..d642500 Binary files /dev/null and b/test/testfiles/gltf/Triangle/glTF/simpleTriangle.bin differ diff --git a/test/testfiles/gltf/Triangle/screenshot/screenshot.png b/test/testfiles/gltf/Triangle/screenshot/screenshot.png new file mode 100644 index 0000000..0c825f3 Binary files /dev/null and b/test/testfiles/gltf/Triangle/screenshot/screenshot.png differ diff --git a/test/testfiles/gltf/Triangle/screenshot/simpleTriangle.png b/test/testfiles/gltf/Triangle/screenshot/simpleTriangle.png new file mode 100644 index 0000000..98df05c Binary files /dev/null and b/test/testfiles/gltf/Triangle/screenshot/simpleTriangle.png differ diff --git a/test/testfiles/gltf/TriangleWithoutIndices/README.md b/test/testfiles/gltf/TriangleWithoutIndices/README.md new file mode 100644 index 0000000..40b03bc --- /dev/null +++ b/test/testfiles/gltf/TriangleWithoutIndices/README.md @@ -0,0 +1,15 @@ +# TriangleWithoutIndices + +## Screenshot + +![screenshot](screenshot/screenshot.png) + +## License Information + +Public domain ([CC0](https://creativecommons.org/publicdomain/zero/1.0/)) + +## Data layout + +The following image shows the data layout of this sample: + +![triangleWithoutIndices](screenshot/triangleWithoutIndices.png) \ No newline at end of file diff --git a/test/testfiles/gltf/TriangleWithoutIndices/glTF-Embedded/TriangleWithoutIndices.gltf b/test/testfiles/gltf/TriangleWithoutIndices/glTF-Embedded/TriangleWithoutIndices.gltf new file mode 100644 index 0000000..1c0736a --- /dev/null +++ b/test/testfiles/gltf/TriangleWithoutIndices/glTF-Embedded/TriangleWithoutIndices.gltf @@ -0,0 +1,54 @@ +{ + "scene" : 0, + "scenes" : [ + { + "nodes" : [ 0 ] + } + ], + + "nodes" : [ + { + "mesh" : 0 + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 0 + } + } ] + } + ], + + "buffers" : [ + { + "uri" : "data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA", + "byteLength" : 36 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 36, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + } + ], + + "asset" : { + "version" : "2.0" + } +} \ No newline at end of file diff --git a/test/testfiles/gltf/TriangleWithoutIndices/glTF/TriangleWithoutIndices.gltf b/test/testfiles/gltf/TriangleWithoutIndices/glTF/TriangleWithoutIndices.gltf new file mode 100644 index 0000000..b9f75cd --- /dev/null +++ b/test/testfiles/gltf/TriangleWithoutIndices/glTF/TriangleWithoutIndices.gltf @@ -0,0 +1,55 @@ +{ + "scene" : 0, + "scenes" : [ + { + "nodes" : [ 0 ] + } + ], + + "nodes" : [ + { + "mesh" : 0 + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 0 + } + } ] + } + ], + + "buffers" : [ + { + "uri" : "triangleWithoutIndices.bin", + "byteLength" : 36 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 36, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + } + ], + + "asset" : { + "version" : "2.0" + } +} + diff --git a/test/testfiles/gltf/TriangleWithoutIndices/glTF/triangleWithoutIndices.bin b/test/testfiles/gltf/TriangleWithoutIndices/glTF/triangleWithoutIndices.bin new file mode 100644 index 0000000..f9f96d8 Binary files /dev/null and b/test/testfiles/gltf/TriangleWithoutIndices/glTF/triangleWithoutIndices.bin differ diff --git a/test/testfiles/gltf/TriangleWithoutIndices/screenshot/screenshot.png b/test/testfiles/gltf/TriangleWithoutIndices/screenshot/screenshot.png new file mode 100644 index 0000000..0c825f3 Binary files /dev/null and b/test/testfiles/gltf/TriangleWithoutIndices/screenshot/screenshot.png differ diff --git a/test/testfiles/gltf/TriangleWithoutIndices/screenshot/triangleWithoutIndices.png b/test/testfiles/gltf/TriangleWithoutIndices/screenshot/triangleWithoutIndices.png new file mode 100644 index 0000000..9851598 Binary files /dev/null and b/test/testfiles/gltf/TriangleWithoutIndices/screenshot/triangleWithoutIndices.png differ diff --git a/test/testfiles/obj/cube with white spaces.mtl b/test/testfiles/obj/cube with white spaces.mtl new file mode 100644 index 0000000..4532a03 --- /dev/null +++ b/test/testfiles/obj/cube with white spaces.mtl @@ -0,0 +1,25 @@ +newmtl Red Color +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 + +newmtl Green Color +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.800000 0.000000 +Ks 0.500000 0.500000 0.500000 + +newmtl Blue Color +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.000000 0.800000 +Ks 0.500000 0.500000 0.500000 + +newmtl White Color +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.800000 0.800000 +Ks 0.500000 0.500000 0.500000 + +newmtl Texture Color +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.800000 0.800000 +Ks 0.500000 0.500000 0.500000 +map_Kd cube_texture.png diff --git a/test/testfiles/obj/cube with white spaces.obj b/test/testfiles/obj/cube with white spaces.obj new file mode 100644 index 0000000..13abd80 --- /dev/null +++ b/test/testfiles/obj/cube with white spaces.obj @@ -0,0 +1,38 @@ +# Cube with Materials + +mtllib cube with white spaces.mtl # comment + +g Cube Mesh + +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +v 0.0 1.0 0.0 +v 0.0 0.0 1.0 +v 1.0 0.0 1.0 +v 1.0 1.0 1.0 +v 0.0 1.0 1.0 + +vn 1.0 0.0 0.0 +vn -1.0 0.0 0.0 +vn 0.0 1.0 0.0 +vn 0.0 -1.0 0.0 +vn 0.0 0.0 1.0 +vn 0.0 0.0 -1.0 + +vt 0.0 0.0 +vt 1.0 0.0 +vt 1.0 1.0 +vt 0.0 1.0 + +usemtl Red Color +f 1/1/4 2/2/4 6/3/4 5/4/4 +usemtl Green Color +f 2/1/1 3/2/1 7/3/1 6/4/1 +usemtl Blue Color +f 3/1/3 4/2/3 8/3/3 7/4/3 +usemtl Texture Color +f 4/1/2 1/2/2 5/3/2 8/4/2 +usemtl White Color +f 1/1/6 4/2/6 3/3/6 2/4/6 +f 5/1/5 6/2/5 7/3/5 8/4/5 diff --git a/test/testfiles/obj/cube_rgb.mtl b/test/testfiles/obj/cube_rgb.mtl new file mode 100644 index 0000000..189b467 --- /dev/null +++ b/test/testfiles/obj/cube_rgb.mtl @@ -0,0 +1,20 @@ +newmtl Red +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 + +newmtl Green +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.800000 0.000000 +Ks 0.500000 0.500000 0.500000 + +newmtl Blue +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.000000 0.800000 +Ks 0.500000 0.500000 0.500000 + +newmtl White +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.800000 0.800000 +Ks 0.500000 0.500000 0.500000 +d 0.5 diff --git a/test/testfiles/obj/cube_rgb.obj b/test/testfiles/obj/cube_rgb.obj new file mode 100644 index 0000000..3c9f46e --- /dev/null +++ b/test/testfiles/obj/cube_rgb.obj @@ -0,0 +1,37 @@ +# Cube with RGB Colors + +mtllib cube_rgb.mtl + +g Cube + +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +v 0.0 1.0 0.0 +v 0.0 0.0 1.0 +v 1.0 0.0 1.0 +v 1.0 1.0 1.0 +v 0.0 1.0 1.0 + +vn 1.0 0.0 0.0 +vn -1.0 0.0 0.0 +vn 0.0 1.0 0.0 +vn 0.0 -1.0 0.0 +vn 0.0 0.0 1.0 +vn 0.0 0.0 -1.0 + +vt 0.0 0.0 +vt 1.0 0.0 +vt 1.0 1.0 +vt 0.0 1.0 + +usemtl Red +f 1/1/4 2/2/4 6/3/4 5/4/4 +usemtl Green +f 2/1/1 3/2/1 7/3/1 6/4/1 +usemtl Blue +f 3/1/3 4/2/3 8/3/3 7/4/3 +usemtl White +f 4/1/2 1/2/2 5/3/2 8/4/2 +f 1/1/6 4/2/6 3/3/6 2/4/6 +f 5/1/5 6/2/5 7/3/5 8/4/5 diff --git a/test/testfiles/obj/cube_texture.png b/test/testfiles/obj/cube_texture.png new file mode 100644 index 0000000..29ce540 Binary files /dev/null and b/test/testfiles/obj/cube_texture.png differ diff --git a/test/testfiles/obj/cube_with_materials.mtl b/test/testfiles/obj/cube_with_materials.mtl new file mode 100644 index 0000000..706a3f3 --- /dev/null +++ b/test/testfiles/obj/cube_with_materials.mtl @@ -0,0 +1,25 @@ +newmtl Red +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 + +newmtl Green +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.800000 0.000000 +Ks 0.500000 0.500000 0.500000 + +newmtl Blue +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.000000 0.800000 +Ks 0.500000 0.500000 0.500000 + +newmtl White +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.800000 0.800000 +Ks 0.500000 0.500000 0.500000 + +newmtl Texture +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.800000 0.800000 +Ks 0.500000 0.500000 0.500000 +map_Kd cube_texture.png diff --git a/test/testfiles/obj/cube_with_materials.obj b/test/testfiles/obj/cube_with_materials.obj new file mode 100644 index 0000000..83558b7 --- /dev/null +++ b/test/testfiles/obj/cube_with_materials.obj @@ -0,0 +1,38 @@ +# Cube with Materials + +mtllib cube_with_materials.mtl + +g Cube + +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +v 0.0 1.0 0.0 +v 0.0 0.0 1.0 +v 1.0 0.0 1.0 +v 1.0 1.0 1.0 +v 0.0 1.0 1.0 + +vn 1.0 0.0 0.0 +vn -1.0 0.0 0.0 +vn 0.0 1.0 0.0 +vn 0.0 -1.0 0.0 +vn 0.0 0.0 1.0 +vn 0.0 0.0 -1.0 + +vt 0.0 0.0 +vt 1.0 0.0 +vt 1.0 1.0 +vt 0.0 1.0 + +usemtl Red +f 1/1/4 2/2/4 6/3/4 5/4/4 +usemtl Green +f 2/1/1 3/2/1 7/3/1 6/4/1 +usemtl Blue +f 3/1/3 4/2/3 8/3/3 7/4/3 +usemtl Texture +f 4/1/2 1/2/2 5/3/2 8/4/2 +usemtl White +f 1/1/6 4/2/6 3/3/6 2/4/6 +f 5/1/5 6/2/5 7/3/5 8/4/5 diff --git a/test/testfiles/obj/cube_with_materials_comments.mtl b/test/testfiles/obj/cube_with_materials_comments.mtl new file mode 100644 index 0000000..706a3f3 --- /dev/null +++ b/test/testfiles/obj/cube_with_materials_comments.mtl @@ -0,0 +1,25 @@ +newmtl Red +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 + +newmtl Green +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.800000 0.000000 +Ks 0.500000 0.500000 0.500000 + +newmtl Blue +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.000000 0.800000 +Ks 0.500000 0.500000 0.500000 + +newmtl White +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.800000 0.800000 +Ks 0.500000 0.500000 0.500000 + +newmtl Texture +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.800000 0.800000 +Ks 0.500000 0.500000 0.500000 +map_Kd cube_texture.png diff --git a/test/testfiles/obj/cube_with_materials_comments.obj b/test/testfiles/obj/cube_with_materials_comments.obj new file mode 100644 index 0000000..449d61a --- /dev/null +++ b/test/testfiles/obj/cube_with_materials_comments.obj @@ -0,0 +1,38 @@ +# Cube with Materials + +mtllib cube_with_materials_comments.mtl # inline comment + +g Cube # inline comment + +v 0.0 0.0 0.0 # inline comment +v 1.0 0.0 0.0 # inline comment +v 1.0 1.0 0.0 +v 0.0 1.0 0.0 +v 0.0 0.0 1.0 +v 1.0 0.0 1.0 +v 1.0 1.0 1.0 +v 0.0 1.0 1.0 + +vn 1.0 0.0 0.0 +vn -1.0 0.0 0.0 # inline comment +vn 0.0 1.0 0.0 +vn 0.0 -1.0 0.0 +vn 0.0 0.0 1.0 # inline comment +vn 0.0 0.0 -1.0 + +vt 0.0 0.0 +vt 1.0 0.0 +vt 1.0 1.0 # inline comment +vt 0.0 1.0 # inline comment + +usemtl Red # inline comment +f 1/1/4 2/2/4 6/3/4 5/4/4 # inline comment +usemtl Green # inline comment +f 2/1/1 3/2/1 7/3/1 6/4/1 # inline comment +usemtl Blue +f 3/1/3 4/2/3 8/3/3 7/4/3 +usemtl Texture +f 4/1/2 1/2/2 5/3/2 8/4/2 +usemtl White +f 1/1/6 4/2/6 3/3/6 2/4/6 +f 5/1/5 6/2/5 7/3/5 8/4/5 diff --git a/test/testfiles/obj/empty_meshes.obj b/test/testfiles/obj/empty_meshes.obj new file mode 100644 index 0000000..6478cd8 --- /dev/null +++ b/test/testfiles/obj/empty_meshes.obj @@ -0,0 +1,16 @@ +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +v 0.0 0.0 1.0 +v 1.0 0.0 1.0 +v 1.0 1.0 1.0 + +g EmptyMesh1 +g MeshName1 +f 1 2 3 + +g EmptyMesh2 +g MeshName2 +f 4 5 6 + +g EmptyMesh3 diff --git a/test/testfiles/obj/hundred_cubes.mtl b/test/testfiles/obj/hundred_cubes.mtl new file mode 100644 index 0000000..cd2c541 --- /dev/null +++ b/test/testfiles/obj/hundred_cubes.mtl @@ -0,0 +1,14 @@ +newmtl Red +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.200000 0.200000 +Ks 0.500000 0.500000 0.500000 + +newmtl Green +Ka 0.000000 0.000000 0.000000 +Kd 0.152940 0.607840 0.380390 +Ks 0.500000 0.500000 0.500000 + +newmtl Blue +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.541180 0.721570 +Ks 0.500000 0.500000 0.500000 diff --git a/test/testfiles/obj/hundred_cubes.obj b/test/testfiles/obj/hundred_cubes.obj new file mode 100644 index 0000000..2061de5 --- /dev/null +++ b/test/testfiles/obj/hundred_cubes.obj @@ -0,0 +1,1603 @@ +mtllib hundred_cubes.mtl + +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +v 0.0 1.0 0.0 +v 0.0 0.0 1.0 +v 1.0 0.0 1.0 +v 1.0 1.0 1.0 +v 0.0 1.0 1.0 +v 0.0 2.0 0.0 +v 1.0 2.0 0.0 +v 1.0 3.0 0.0 +v 0.0 3.0 0.0 +v 0.0 2.0 1.0 +v 1.0 2.0 1.0 +v 1.0 3.0 1.0 +v 0.0 3.0 1.0 +v 0.0 4.0 0.0 +v 1.0 4.0 0.0 +v 1.0 5.0 0.0 +v 0.0 5.0 0.0 +v 0.0 4.0 1.0 +v 1.0 4.0 1.0 +v 1.0 5.0 1.0 +v 0.0 5.0 1.0 +v 0.0 6.0 0.0 +v 1.0 6.0 0.0 +v 1.0 7.0 0.0 +v 0.0 7.0 0.0 +v 0.0 6.0 1.0 +v 1.0 6.0 1.0 +v 1.0 7.0 1.0 +v 0.0 7.0 1.0 +v 0.0 8.0 0.0 +v 1.0 8.0 0.0 +v 1.0 9.0 0.0 +v 0.0 9.0 0.0 +v 0.0 8.0 1.0 +v 1.0 8.0 1.0 +v 1.0 9.0 1.0 +v 0.0 9.0 1.0 +v 0.0 10.0 0.0 +v 1.0 10.0 0.0 +v 1.0 11.0 0.0 +v 0.0 11.0 0.0 +v 0.0 10.0 1.0 +v 1.0 10.0 1.0 +v 1.0 11.0 1.0 +v 0.0 11.0 1.0 +v 0.0 12.0 0.0 +v 1.0 12.0 0.0 +v 1.0 13.0 0.0 +v 0.0 13.0 0.0 +v 0.0 12.0 1.0 +v 1.0 12.0 1.0 +v 1.0 13.0 1.0 +v 0.0 13.0 1.0 +v 0.0 14.0 0.0 +v 1.0 14.0 0.0 +v 1.0 15.0 0.0 +v 0.0 15.0 0.0 +v 0.0 14.0 1.0 +v 1.0 14.0 1.0 +v 1.0 15.0 1.0 +v 0.0 15.0 1.0 +v 0.0 16.0 0.0 +v 1.0 16.0 0.0 +v 1.0 17.0 0.0 +v 0.0 17.0 0.0 +v 0.0 16.0 1.0 +v 1.0 16.0 1.0 +v 1.0 17.0 1.0 +v 0.0 17.0 1.0 +v 0.0 18.0 0.0 +v 1.0 18.0 0.0 +v 1.0 19.0 0.0 +v 0.0 19.0 0.0 +v 0.0 18.0 1.0 +v 1.0 18.0 1.0 +v 1.0 19.0 1.0 +v 0.0 19.0 1.0 +v 2.0 0.0 0.0 +v 3.0 0.0 0.0 +v 3.0 1.0 0.0 +v 2.0 1.0 0.0 +v 2.0 0.0 1.0 +v 3.0 0.0 1.0 +v 3.0 1.0 1.0 +v 2.0 1.0 1.0 +v 2.0 2.0 0.0 +v 3.0 2.0 0.0 +v 3.0 3.0 0.0 +v 2.0 3.0 0.0 +v 2.0 2.0 1.0 +v 3.0 2.0 1.0 +v 3.0 3.0 1.0 +v 2.0 3.0 1.0 +v 2.0 4.0 0.0 +v 3.0 4.0 0.0 +v 3.0 5.0 0.0 +v 2.0 5.0 0.0 +v 2.0 4.0 1.0 +v 3.0 4.0 1.0 +v 3.0 5.0 1.0 +v 2.0 5.0 1.0 +v 2.0 6.0 0.0 +v 3.0 6.0 0.0 +v 3.0 7.0 0.0 +v 2.0 7.0 0.0 +v 2.0 6.0 1.0 +v 3.0 6.0 1.0 +v 3.0 7.0 1.0 +v 2.0 7.0 1.0 +v 2.0 8.0 0.0 +v 3.0 8.0 0.0 +v 3.0 9.0 0.0 +v 2.0 9.0 0.0 +v 2.0 8.0 1.0 +v 3.0 8.0 1.0 +v 3.0 9.0 1.0 +v 2.0 9.0 1.0 +v 2.0 10.0 0.0 +v 3.0 10.0 0.0 +v 3.0 11.0 0.0 +v 2.0 11.0 0.0 +v 2.0 10.0 1.0 +v 3.0 10.0 1.0 +v 3.0 11.0 1.0 +v 2.0 11.0 1.0 +v 2.0 12.0 0.0 +v 3.0 12.0 0.0 +v 3.0 13.0 0.0 +v 2.0 13.0 0.0 +v 2.0 12.0 1.0 +v 3.0 12.0 1.0 +v 3.0 13.0 1.0 +v 2.0 13.0 1.0 +v 2.0 14.0 0.0 +v 3.0 14.0 0.0 +v 3.0 15.0 0.0 +v 2.0 15.0 0.0 +v 2.0 14.0 1.0 +v 3.0 14.0 1.0 +v 3.0 15.0 1.0 +v 2.0 15.0 1.0 +v 2.0 16.0 0.0 +v 3.0 16.0 0.0 +v 3.0 17.0 0.0 +v 2.0 17.0 0.0 +v 2.0 16.0 1.0 +v 3.0 16.0 1.0 +v 3.0 17.0 1.0 +v 2.0 17.0 1.0 +v 2.0 18.0 0.0 +v 3.0 18.0 0.0 +v 3.0 19.0 0.0 +v 2.0 19.0 0.0 +v 2.0 18.0 1.0 +v 3.0 18.0 1.0 +v 3.0 19.0 1.0 +v 2.0 19.0 1.0 +v 4.0 0.0 0.0 +v 5.0 0.0 0.0 +v 5.0 1.0 0.0 +v 4.0 1.0 0.0 +v 4.0 0.0 1.0 +v 5.0 0.0 1.0 +v 5.0 1.0 1.0 +v 4.0 1.0 1.0 +v 4.0 2.0 0.0 +v 5.0 2.0 0.0 +v 5.0 3.0 0.0 +v 4.0 3.0 0.0 +v 4.0 2.0 1.0 +v 5.0 2.0 1.0 +v 5.0 3.0 1.0 +v 4.0 3.0 1.0 +v 4.0 4.0 0.0 +v 5.0 4.0 0.0 +v 5.0 5.0 0.0 +v 4.0 5.0 0.0 +v 4.0 4.0 1.0 +v 5.0 4.0 1.0 +v 5.0 5.0 1.0 +v 4.0 5.0 1.0 +v 4.0 6.0 0.0 +v 5.0 6.0 0.0 +v 5.0 7.0 0.0 +v 4.0 7.0 0.0 +v 4.0 6.0 1.0 +v 5.0 6.0 1.0 +v 5.0 7.0 1.0 +v 4.0 7.0 1.0 +v 4.0 8.0 0.0 +v 5.0 8.0 0.0 +v 5.0 9.0 0.0 +v 4.0 9.0 0.0 +v 4.0 8.0 1.0 +v 5.0 8.0 1.0 +v 5.0 9.0 1.0 +v 4.0 9.0 1.0 +v 4.0 10.0 0.0 +v 5.0 10.0 0.0 +v 5.0 11.0 0.0 +v 4.0 11.0 0.0 +v 4.0 10.0 1.0 +v 5.0 10.0 1.0 +v 5.0 11.0 1.0 +v 4.0 11.0 1.0 +v 4.0 12.0 0.0 +v 5.0 12.0 0.0 +v 5.0 13.0 0.0 +v 4.0 13.0 0.0 +v 4.0 12.0 1.0 +v 5.0 12.0 1.0 +v 5.0 13.0 1.0 +v 4.0 13.0 1.0 +v 4.0 14.0 0.0 +v 5.0 14.0 0.0 +v 5.0 15.0 0.0 +v 4.0 15.0 0.0 +v 4.0 14.0 1.0 +v 5.0 14.0 1.0 +v 5.0 15.0 1.0 +v 4.0 15.0 1.0 +v 4.0 16.0 0.0 +v 5.0 16.0 0.0 +v 5.0 17.0 0.0 +v 4.0 17.0 0.0 +v 4.0 16.0 1.0 +v 5.0 16.0 1.0 +v 5.0 17.0 1.0 +v 4.0 17.0 1.0 +v 4.0 18.0 0.0 +v 5.0 18.0 0.0 +v 5.0 19.0 0.0 +v 4.0 19.0 0.0 +v 4.0 18.0 1.0 +v 5.0 18.0 1.0 +v 5.0 19.0 1.0 +v 4.0 19.0 1.0 +v 6.0 0.0 0.0 +v 7.0 0.0 0.0 +v 7.0 1.0 0.0 +v 6.0 1.0 0.0 +v 6.0 0.0 1.0 +v 7.0 0.0 1.0 +v 7.0 1.0 1.0 +v 6.0 1.0 1.0 +v 6.0 2.0 0.0 +v 7.0 2.0 0.0 +v 7.0 3.0 0.0 +v 6.0 3.0 0.0 +v 6.0 2.0 1.0 +v 7.0 2.0 1.0 +v 7.0 3.0 1.0 +v 6.0 3.0 1.0 +v 6.0 4.0 0.0 +v 7.0 4.0 0.0 +v 7.0 5.0 0.0 +v 6.0 5.0 0.0 +v 6.0 4.0 1.0 +v 7.0 4.0 1.0 +v 7.0 5.0 1.0 +v 6.0 5.0 1.0 +v 6.0 6.0 0.0 +v 7.0 6.0 0.0 +v 7.0 7.0 0.0 +v 6.0 7.0 0.0 +v 6.0 6.0 1.0 +v 7.0 6.0 1.0 +v 7.0 7.0 1.0 +v 6.0 7.0 1.0 +v 6.0 8.0 0.0 +v 7.0 8.0 0.0 +v 7.0 9.0 0.0 +v 6.0 9.0 0.0 +v 6.0 8.0 1.0 +v 7.0 8.0 1.0 +v 7.0 9.0 1.0 +v 6.0 9.0 1.0 +v 6.0 10.0 0.0 +v 7.0 10.0 0.0 +v 7.0 11.0 0.0 +v 6.0 11.0 0.0 +v 6.0 10.0 1.0 +v 7.0 10.0 1.0 +v 7.0 11.0 1.0 +v 6.0 11.0 1.0 +v 6.0 12.0 0.0 +v 7.0 12.0 0.0 +v 7.0 13.0 0.0 +v 6.0 13.0 0.0 +v 6.0 12.0 1.0 +v 7.0 12.0 1.0 +v 7.0 13.0 1.0 +v 6.0 13.0 1.0 +v 6.0 14.0 0.0 +v 7.0 14.0 0.0 +v 7.0 15.0 0.0 +v 6.0 15.0 0.0 +v 6.0 14.0 1.0 +v 7.0 14.0 1.0 +v 7.0 15.0 1.0 +v 6.0 15.0 1.0 +v 6.0 16.0 0.0 +v 7.0 16.0 0.0 +v 7.0 17.0 0.0 +v 6.0 17.0 0.0 +v 6.0 16.0 1.0 +v 7.0 16.0 1.0 +v 7.0 17.0 1.0 +v 6.0 17.0 1.0 +v 6.0 18.0 0.0 +v 7.0 18.0 0.0 +v 7.0 19.0 0.0 +v 6.0 19.0 0.0 +v 6.0 18.0 1.0 +v 7.0 18.0 1.0 +v 7.0 19.0 1.0 +v 6.0 19.0 1.0 +v 8.0 0.0 0.0 +v 9.0 0.0 0.0 +v 9.0 1.0 0.0 +v 8.0 1.0 0.0 +v 8.0 0.0 1.0 +v 9.0 0.0 1.0 +v 9.0 1.0 1.0 +v 8.0 1.0 1.0 +v 8.0 2.0 0.0 +v 9.0 2.0 0.0 +v 9.0 3.0 0.0 +v 8.0 3.0 0.0 +v 8.0 2.0 1.0 +v 9.0 2.0 1.0 +v 9.0 3.0 1.0 +v 8.0 3.0 1.0 +v 8.0 4.0 0.0 +v 9.0 4.0 0.0 +v 9.0 5.0 0.0 +v 8.0 5.0 0.0 +v 8.0 4.0 1.0 +v 9.0 4.0 1.0 +v 9.0 5.0 1.0 +v 8.0 5.0 1.0 +v 8.0 6.0 0.0 +v 9.0 6.0 0.0 +v 9.0 7.0 0.0 +v 8.0 7.0 0.0 +v 8.0 6.0 1.0 +v 9.0 6.0 1.0 +v 9.0 7.0 1.0 +v 8.0 7.0 1.0 +v 8.0 8.0 0.0 +v 9.0 8.0 0.0 +v 9.0 9.0 0.0 +v 8.0 9.0 0.0 +v 8.0 8.0 1.0 +v 9.0 8.0 1.0 +v 9.0 9.0 1.0 +v 8.0 9.0 1.0 +v 8.0 10.0 0.0 +v 9.0 10.0 0.0 +v 9.0 11.0 0.0 +v 8.0 11.0 0.0 +v 8.0 10.0 1.0 +v 9.0 10.0 1.0 +v 9.0 11.0 1.0 +v 8.0 11.0 1.0 +v 8.0 12.0 0.0 +v 9.0 12.0 0.0 +v 9.0 13.0 0.0 +v 8.0 13.0 0.0 +v 8.0 12.0 1.0 +v 9.0 12.0 1.0 +v 9.0 13.0 1.0 +v 8.0 13.0 1.0 +v 8.0 14.0 0.0 +v 9.0 14.0 0.0 +v 9.0 15.0 0.0 +v 8.0 15.0 0.0 +v 8.0 14.0 1.0 +v 9.0 14.0 1.0 +v 9.0 15.0 1.0 +v 8.0 15.0 1.0 +v 8.0 16.0 0.0 +v 9.0 16.0 0.0 +v 9.0 17.0 0.0 +v 8.0 17.0 0.0 +v 8.0 16.0 1.0 +v 9.0 16.0 1.0 +v 9.0 17.0 1.0 +v 8.0 17.0 1.0 +v 8.0 18.0 0.0 +v 9.0 18.0 0.0 +v 9.0 19.0 0.0 +v 8.0 19.0 0.0 +v 8.0 18.0 1.0 +v 9.0 18.0 1.0 +v 9.0 19.0 1.0 +v 8.0 19.0 1.0 +v 10.0 0.0 0.0 +v 11.0 0.0 0.0 +v 11.0 1.0 0.0 +v 10.0 1.0 0.0 +v 10.0 0.0 1.0 +v 11.0 0.0 1.0 +v 11.0 1.0 1.0 +v 10.0 1.0 1.0 +v 10.0 2.0 0.0 +v 11.0 2.0 0.0 +v 11.0 3.0 0.0 +v 10.0 3.0 0.0 +v 10.0 2.0 1.0 +v 11.0 2.0 1.0 +v 11.0 3.0 1.0 +v 10.0 3.0 1.0 +v 10.0 4.0 0.0 +v 11.0 4.0 0.0 +v 11.0 5.0 0.0 +v 10.0 5.0 0.0 +v 10.0 4.0 1.0 +v 11.0 4.0 1.0 +v 11.0 5.0 1.0 +v 10.0 5.0 1.0 +v 10.0 6.0 0.0 +v 11.0 6.0 0.0 +v 11.0 7.0 0.0 +v 10.0 7.0 0.0 +v 10.0 6.0 1.0 +v 11.0 6.0 1.0 +v 11.0 7.0 1.0 +v 10.0 7.0 1.0 +v 10.0 8.0 0.0 +v 11.0 8.0 0.0 +v 11.0 9.0 0.0 +v 10.0 9.0 0.0 +v 10.0 8.0 1.0 +v 11.0 8.0 1.0 +v 11.0 9.0 1.0 +v 10.0 9.0 1.0 +v 10.0 10.0 0.0 +v 11.0 10.0 0.0 +v 11.0 11.0 0.0 +v 10.0 11.0 0.0 +v 10.0 10.0 1.0 +v 11.0 10.0 1.0 +v 11.0 11.0 1.0 +v 10.0 11.0 1.0 +v 10.0 12.0 0.0 +v 11.0 12.0 0.0 +v 11.0 13.0 0.0 +v 10.0 13.0 0.0 +v 10.0 12.0 1.0 +v 11.0 12.0 1.0 +v 11.0 13.0 1.0 +v 10.0 13.0 1.0 +v 10.0 14.0 0.0 +v 11.0 14.0 0.0 +v 11.0 15.0 0.0 +v 10.0 15.0 0.0 +v 10.0 14.0 1.0 +v 11.0 14.0 1.0 +v 11.0 15.0 1.0 +v 10.0 15.0 1.0 +v 10.0 16.0 0.0 +v 11.0 16.0 0.0 +v 11.0 17.0 0.0 +v 10.0 17.0 0.0 +v 10.0 16.0 1.0 +v 11.0 16.0 1.0 +v 11.0 17.0 1.0 +v 10.0 17.0 1.0 +v 10.0 18.0 0.0 +v 11.0 18.0 0.0 +v 11.0 19.0 0.0 +v 10.0 19.0 0.0 +v 10.0 18.0 1.0 +v 11.0 18.0 1.0 +v 11.0 19.0 1.0 +v 10.0 19.0 1.0 +v 12.0 0.0 0.0 +v 13.0 0.0 0.0 +v 13.0 1.0 0.0 +v 12.0 1.0 0.0 +v 12.0 0.0 1.0 +v 13.0 0.0 1.0 +v 13.0 1.0 1.0 +v 12.0 1.0 1.0 +v 12.0 2.0 0.0 +v 13.0 2.0 0.0 +v 13.0 3.0 0.0 +v 12.0 3.0 0.0 +v 12.0 2.0 1.0 +v 13.0 2.0 1.0 +v 13.0 3.0 1.0 +v 12.0 3.0 1.0 +v 12.0 4.0 0.0 +v 13.0 4.0 0.0 +v 13.0 5.0 0.0 +v 12.0 5.0 0.0 +v 12.0 4.0 1.0 +v 13.0 4.0 1.0 +v 13.0 5.0 1.0 +v 12.0 5.0 1.0 +v 12.0 6.0 0.0 +v 13.0 6.0 0.0 +v 13.0 7.0 0.0 +v 12.0 7.0 0.0 +v 12.0 6.0 1.0 +v 13.0 6.0 1.0 +v 13.0 7.0 1.0 +v 12.0 7.0 1.0 +v 12.0 8.0 0.0 +v 13.0 8.0 0.0 +v 13.0 9.0 0.0 +v 12.0 9.0 0.0 +v 12.0 8.0 1.0 +v 13.0 8.0 1.0 +v 13.0 9.0 1.0 +v 12.0 9.0 1.0 +v 12.0 10.0 0.0 +v 13.0 10.0 0.0 +v 13.0 11.0 0.0 +v 12.0 11.0 0.0 +v 12.0 10.0 1.0 +v 13.0 10.0 1.0 +v 13.0 11.0 1.0 +v 12.0 11.0 1.0 +v 12.0 12.0 0.0 +v 13.0 12.0 0.0 +v 13.0 13.0 0.0 +v 12.0 13.0 0.0 +v 12.0 12.0 1.0 +v 13.0 12.0 1.0 +v 13.0 13.0 1.0 +v 12.0 13.0 1.0 +v 12.0 14.0 0.0 +v 13.0 14.0 0.0 +v 13.0 15.0 0.0 +v 12.0 15.0 0.0 +v 12.0 14.0 1.0 +v 13.0 14.0 1.0 +v 13.0 15.0 1.0 +v 12.0 15.0 1.0 +v 12.0 16.0 0.0 +v 13.0 16.0 0.0 +v 13.0 17.0 0.0 +v 12.0 17.0 0.0 +v 12.0 16.0 1.0 +v 13.0 16.0 1.0 +v 13.0 17.0 1.0 +v 12.0 17.0 1.0 +v 12.0 18.0 0.0 +v 13.0 18.0 0.0 +v 13.0 19.0 0.0 +v 12.0 19.0 0.0 +v 12.0 18.0 1.0 +v 13.0 18.0 1.0 +v 13.0 19.0 1.0 +v 12.0 19.0 1.0 +v 14.0 0.0 0.0 +v 15.0 0.0 0.0 +v 15.0 1.0 0.0 +v 14.0 1.0 0.0 +v 14.0 0.0 1.0 +v 15.0 0.0 1.0 +v 15.0 1.0 1.0 +v 14.0 1.0 1.0 +v 14.0 2.0 0.0 +v 15.0 2.0 0.0 +v 15.0 3.0 0.0 +v 14.0 3.0 0.0 +v 14.0 2.0 1.0 +v 15.0 2.0 1.0 +v 15.0 3.0 1.0 +v 14.0 3.0 1.0 +v 14.0 4.0 0.0 +v 15.0 4.0 0.0 +v 15.0 5.0 0.0 +v 14.0 5.0 0.0 +v 14.0 4.0 1.0 +v 15.0 4.0 1.0 +v 15.0 5.0 1.0 +v 14.0 5.0 1.0 +v 14.0 6.0 0.0 +v 15.0 6.0 0.0 +v 15.0 7.0 0.0 +v 14.0 7.0 0.0 +v 14.0 6.0 1.0 +v 15.0 6.0 1.0 +v 15.0 7.0 1.0 +v 14.0 7.0 1.0 +v 14.0 8.0 0.0 +v 15.0 8.0 0.0 +v 15.0 9.0 0.0 +v 14.0 9.0 0.0 +v 14.0 8.0 1.0 +v 15.0 8.0 1.0 +v 15.0 9.0 1.0 +v 14.0 9.0 1.0 +v 14.0 10.0 0.0 +v 15.0 10.0 0.0 +v 15.0 11.0 0.0 +v 14.0 11.0 0.0 +v 14.0 10.0 1.0 +v 15.0 10.0 1.0 +v 15.0 11.0 1.0 +v 14.0 11.0 1.0 +v 14.0 12.0 0.0 +v 15.0 12.0 0.0 +v 15.0 13.0 0.0 +v 14.0 13.0 0.0 +v 14.0 12.0 1.0 +v 15.0 12.0 1.0 +v 15.0 13.0 1.0 +v 14.0 13.0 1.0 +v 14.0 14.0 0.0 +v 15.0 14.0 0.0 +v 15.0 15.0 0.0 +v 14.0 15.0 0.0 +v 14.0 14.0 1.0 +v 15.0 14.0 1.0 +v 15.0 15.0 1.0 +v 14.0 15.0 1.0 +v 14.0 16.0 0.0 +v 15.0 16.0 0.0 +v 15.0 17.0 0.0 +v 14.0 17.0 0.0 +v 14.0 16.0 1.0 +v 15.0 16.0 1.0 +v 15.0 17.0 1.0 +v 14.0 17.0 1.0 +v 14.0 18.0 0.0 +v 15.0 18.0 0.0 +v 15.0 19.0 0.0 +v 14.0 19.0 0.0 +v 14.0 18.0 1.0 +v 15.0 18.0 1.0 +v 15.0 19.0 1.0 +v 14.0 19.0 1.0 +v 16.0 0.0 0.0 +v 17.0 0.0 0.0 +v 17.0 1.0 0.0 +v 16.0 1.0 0.0 +v 16.0 0.0 1.0 +v 17.0 0.0 1.0 +v 17.0 1.0 1.0 +v 16.0 1.0 1.0 +v 16.0 2.0 0.0 +v 17.0 2.0 0.0 +v 17.0 3.0 0.0 +v 16.0 3.0 0.0 +v 16.0 2.0 1.0 +v 17.0 2.0 1.0 +v 17.0 3.0 1.0 +v 16.0 3.0 1.0 +v 16.0 4.0 0.0 +v 17.0 4.0 0.0 +v 17.0 5.0 0.0 +v 16.0 5.0 0.0 +v 16.0 4.0 1.0 +v 17.0 4.0 1.0 +v 17.0 5.0 1.0 +v 16.0 5.0 1.0 +v 16.0 6.0 0.0 +v 17.0 6.0 0.0 +v 17.0 7.0 0.0 +v 16.0 7.0 0.0 +v 16.0 6.0 1.0 +v 17.0 6.0 1.0 +v 17.0 7.0 1.0 +v 16.0 7.0 1.0 +v 16.0 8.0 0.0 +v 17.0 8.0 0.0 +v 17.0 9.0 0.0 +v 16.0 9.0 0.0 +v 16.0 8.0 1.0 +v 17.0 8.0 1.0 +v 17.0 9.0 1.0 +v 16.0 9.0 1.0 +v 16.0 10.0 0.0 +v 17.0 10.0 0.0 +v 17.0 11.0 0.0 +v 16.0 11.0 0.0 +v 16.0 10.0 1.0 +v 17.0 10.0 1.0 +v 17.0 11.0 1.0 +v 16.0 11.0 1.0 +v 16.0 12.0 0.0 +v 17.0 12.0 0.0 +v 17.0 13.0 0.0 +v 16.0 13.0 0.0 +v 16.0 12.0 1.0 +v 17.0 12.0 1.0 +v 17.0 13.0 1.0 +v 16.0 13.0 1.0 +v 16.0 14.0 0.0 +v 17.0 14.0 0.0 +v 17.0 15.0 0.0 +v 16.0 15.0 0.0 +v 16.0 14.0 1.0 +v 17.0 14.0 1.0 +v 17.0 15.0 1.0 +v 16.0 15.0 1.0 +v 16.0 16.0 0.0 +v 17.0 16.0 0.0 +v 17.0 17.0 0.0 +v 16.0 17.0 0.0 +v 16.0 16.0 1.0 +v 17.0 16.0 1.0 +v 17.0 17.0 1.0 +v 16.0 17.0 1.0 +v 16.0 18.0 0.0 +v 17.0 18.0 0.0 +v 17.0 19.0 0.0 +v 16.0 19.0 0.0 +v 16.0 18.0 1.0 +v 17.0 18.0 1.0 +v 17.0 19.0 1.0 +v 16.0 19.0 1.0 +v 18.0 0.0 0.0 +v 19.0 0.0 0.0 +v 19.0 1.0 0.0 +v 18.0 1.0 0.0 +v 18.0 0.0 1.0 +v 19.0 0.0 1.0 +v 19.0 1.0 1.0 +v 18.0 1.0 1.0 +v 18.0 2.0 0.0 +v 19.0 2.0 0.0 +v 19.0 3.0 0.0 +v 18.0 3.0 0.0 +v 18.0 2.0 1.0 +v 19.0 2.0 1.0 +v 19.0 3.0 1.0 +v 18.0 3.0 1.0 +v 18.0 4.0 0.0 +v 19.0 4.0 0.0 +v 19.0 5.0 0.0 +v 18.0 5.0 0.0 +v 18.0 4.0 1.0 +v 19.0 4.0 1.0 +v 19.0 5.0 1.0 +v 18.0 5.0 1.0 +v 18.0 6.0 0.0 +v 19.0 6.0 0.0 +v 19.0 7.0 0.0 +v 18.0 7.0 0.0 +v 18.0 6.0 1.0 +v 19.0 6.0 1.0 +v 19.0 7.0 1.0 +v 18.0 7.0 1.0 +v 18.0 8.0 0.0 +v 19.0 8.0 0.0 +v 19.0 9.0 0.0 +v 18.0 9.0 0.0 +v 18.0 8.0 1.0 +v 19.0 8.0 1.0 +v 19.0 9.0 1.0 +v 18.0 9.0 1.0 +v 18.0 10.0 0.0 +v 19.0 10.0 0.0 +v 19.0 11.0 0.0 +v 18.0 11.0 0.0 +v 18.0 10.0 1.0 +v 19.0 10.0 1.0 +v 19.0 11.0 1.0 +v 18.0 11.0 1.0 +v 18.0 12.0 0.0 +v 19.0 12.0 0.0 +v 19.0 13.0 0.0 +v 18.0 13.0 0.0 +v 18.0 12.0 1.0 +v 19.0 12.0 1.0 +v 19.0 13.0 1.0 +v 18.0 13.0 1.0 +v 18.0 14.0 0.0 +v 19.0 14.0 0.0 +v 19.0 15.0 0.0 +v 18.0 15.0 0.0 +v 18.0 14.0 1.0 +v 19.0 14.0 1.0 +v 19.0 15.0 1.0 +v 18.0 15.0 1.0 +v 18.0 16.0 0.0 +v 19.0 16.0 0.0 +v 19.0 17.0 0.0 +v 18.0 17.0 0.0 +v 18.0 16.0 1.0 +v 19.0 16.0 1.0 +v 19.0 17.0 1.0 +v 18.0 17.0 1.0 +v 18.0 18.0 0.0 +v 19.0 18.0 0.0 +v 19.0 19.0 0.0 +v 18.0 19.0 0.0 +v 18.0 18.0 1.0 +v 19.0 18.0 1.0 +v 19.0 19.0 1.0 +v 18.0 19.0 1.0 + +g Mesh001 +usemtl Red +f 1 2 6 5 +f 2 3 7 6 +f 3 4 8 7 +f 4 1 5 8 +f 1 4 3 2 +f 5 6 7 8 +g Mesh002 +usemtl Green +f 9 10 14 13 +f 10 11 15 14 +f 11 12 16 15 +f 12 9 13 16 +f 9 12 11 10 +f 13 14 15 16 +g Mesh003 +usemtl Blue +f 17 18 22 21 +f 18 19 23 22 +f 19 20 24 23 +f 20 17 21 24 +f 17 20 19 18 +f 21 22 23 24 +g Mesh004 +usemtl Red +f 25 26 30 29 +f 26 27 31 30 +f 27 28 32 31 +f 28 25 29 32 +f 25 28 27 26 +f 29 30 31 32 +g Mesh005 +usemtl Green +f 33 34 38 37 +f 34 35 39 38 +f 35 36 40 39 +f 36 33 37 40 +f 33 36 35 34 +f 37 38 39 40 +g Mesh006 +usemtl Blue +f 41 42 46 45 +f 42 43 47 46 +f 43 44 48 47 +f 44 41 45 48 +f 41 44 43 42 +f 45 46 47 48 +g Mesh007 +usemtl Red +f 49 50 54 53 +f 50 51 55 54 +f 51 52 56 55 +f 52 49 53 56 +f 49 52 51 50 +f 53 54 55 56 +g Mesh008 +usemtl Green +f 57 58 62 61 +f 58 59 63 62 +f 59 60 64 63 +f 60 57 61 64 +f 57 60 59 58 +f 61 62 63 64 +g Mesh009 +usemtl Blue +f 65 66 70 69 +f 66 67 71 70 +f 67 68 72 71 +f 68 65 69 72 +f 65 68 67 66 +f 69 70 71 72 +g Mesh010 +usemtl Red +f 73 74 78 77 +f 74 75 79 78 +f 75 76 80 79 +f 76 73 77 80 +f 73 76 75 74 +f 77 78 79 80 +g Mesh011 +usemtl Green +f 81 82 86 85 +f 82 83 87 86 +f 83 84 88 87 +f 84 81 85 88 +f 81 84 83 82 +f 85 86 87 88 +g Mesh012 +usemtl Blue +f 89 90 94 93 +f 90 91 95 94 +f 91 92 96 95 +f 92 89 93 96 +f 89 92 91 90 +f 93 94 95 96 +g Mesh013 +usemtl Red +f 97 98 102 101 +f 98 99 103 102 +f 99 100 104 103 +f 100 97 101 104 +f 97 100 99 98 +f 101 102 103 104 +g Mesh014 +usemtl Green +f 105 106 110 109 +f 106 107 111 110 +f 107 108 112 111 +f 108 105 109 112 +f 105 108 107 106 +f 109 110 111 112 +g Mesh015 +usemtl Blue +f 113 114 118 117 +f 114 115 119 118 +f 115 116 120 119 +f 116 113 117 120 +f 113 116 115 114 +f 117 118 119 120 +g Mesh016 +usemtl Red +f 121 122 126 125 +f 122 123 127 126 +f 123 124 128 127 +f 124 121 125 128 +f 121 124 123 122 +f 125 126 127 128 +g Mesh017 +usemtl Green +f 129 130 134 133 +f 130 131 135 134 +f 131 132 136 135 +f 132 129 133 136 +f 129 132 131 130 +f 133 134 135 136 +g Mesh018 +usemtl Blue +f 137 138 142 141 +f 138 139 143 142 +f 139 140 144 143 +f 140 137 141 144 +f 137 140 139 138 +f 141 142 143 144 +g Mesh019 +usemtl Red +f 145 146 150 149 +f 146 147 151 150 +f 147 148 152 151 +f 148 145 149 152 +f 145 148 147 146 +f 149 150 151 152 +g Mesh020 +usemtl Green +f 153 154 158 157 +f 154 155 159 158 +f 155 156 160 159 +f 156 153 157 160 +f 153 156 155 154 +f 157 158 159 160 +g Mesh021 +usemtl Blue +f 161 162 166 165 +f 162 163 167 166 +f 163 164 168 167 +f 164 161 165 168 +f 161 164 163 162 +f 165 166 167 168 +g Mesh022 +usemtl Red +f 169 170 174 173 +f 170 171 175 174 +f 171 172 176 175 +f 172 169 173 176 +f 169 172 171 170 +f 173 174 175 176 +g Mesh023 +usemtl Green +f 177 178 182 181 +f 178 179 183 182 +f 179 180 184 183 +f 180 177 181 184 +f 177 180 179 178 +f 181 182 183 184 +g Mesh024 +usemtl Blue +f 185 186 190 189 +f 186 187 191 190 +f 187 188 192 191 +f 188 185 189 192 +f 185 188 187 186 +f 189 190 191 192 +g Mesh025 +usemtl Red +f 193 194 198 197 +f 194 195 199 198 +f 195 196 200 199 +f 196 193 197 200 +f 193 196 195 194 +f 197 198 199 200 +g Mesh026 +usemtl Green +f 201 202 206 205 +f 202 203 207 206 +f 203 204 208 207 +f 204 201 205 208 +f 201 204 203 202 +f 205 206 207 208 +g Mesh027 +usemtl Blue +f 209 210 214 213 +f 210 211 215 214 +f 211 212 216 215 +f 212 209 213 216 +f 209 212 211 210 +f 213 214 215 216 +g Mesh028 +usemtl Red +f 217 218 222 221 +f 218 219 223 222 +f 219 220 224 223 +f 220 217 221 224 +f 217 220 219 218 +f 221 222 223 224 +g Mesh029 +usemtl Green +f 225 226 230 229 +f 226 227 231 230 +f 227 228 232 231 +f 228 225 229 232 +f 225 228 227 226 +f 229 230 231 232 +g Mesh030 +usemtl Blue +f 233 234 238 237 +f 234 235 239 238 +f 235 236 240 239 +f 236 233 237 240 +f 233 236 235 234 +f 237 238 239 240 +g Mesh031 +usemtl Red +f 241 242 246 245 +f 242 243 247 246 +f 243 244 248 247 +f 244 241 245 248 +f 241 244 243 242 +f 245 246 247 248 +g Mesh032 +usemtl Green +f 249 250 254 253 +f 250 251 255 254 +f 251 252 256 255 +f 252 249 253 256 +f 249 252 251 250 +f 253 254 255 256 +g Mesh033 +usemtl Blue +f 257 258 262 261 +f 258 259 263 262 +f 259 260 264 263 +f 260 257 261 264 +f 257 260 259 258 +f 261 262 263 264 +g Mesh034 +usemtl Red +f 265 266 270 269 +f 266 267 271 270 +f 267 268 272 271 +f 268 265 269 272 +f 265 268 267 266 +f 269 270 271 272 +g Mesh035 +usemtl Green +f 273 274 278 277 +f 274 275 279 278 +f 275 276 280 279 +f 276 273 277 280 +f 273 276 275 274 +f 277 278 279 280 +g Mesh036 +usemtl Blue +f 281 282 286 285 +f 282 283 287 286 +f 283 284 288 287 +f 284 281 285 288 +f 281 284 283 282 +f 285 286 287 288 +g Mesh037 +usemtl Red +f 289 290 294 293 +f 290 291 295 294 +f 291 292 296 295 +f 292 289 293 296 +f 289 292 291 290 +f 293 294 295 296 +g Mesh038 +usemtl Green +f 297 298 302 301 +f 298 299 303 302 +f 299 300 304 303 +f 300 297 301 304 +f 297 300 299 298 +f 301 302 303 304 +g Mesh039 +usemtl Blue +f 305 306 310 309 +f 306 307 311 310 +f 307 308 312 311 +f 308 305 309 312 +f 305 308 307 306 +f 309 310 311 312 +g Mesh040 +usemtl Red +f 313 314 318 317 +f 314 315 319 318 +f 315 316 320 319 +f 316 313 317 320 +f 313 316 315 314 +f 317 318 319 320 +g Mesh041 +usemtl Green +f 321 322 326 325 +f 322 323 327 326 +f 323 324 328 327 +f 324 321 325 328 +f 321 324 323 322 +f 325 326 327 328 +g Mesh042 +usemtl Blue +f 329 330 334 333 +f 330 331 335 334 +f 331 332 336 335 +f 332 329 333 336 +f 329 332 331 330 +f 333 334 335 336 +g Mesh043 +usemtl Red +f 337 338 342 341 +f 338 339 343 342 +f 339 340 344 343 +f 340 337 341 344 +f 337 340 339 338 +f 341 342 343 344 +g Mesh044 +usemtl Green +f 345 346 350 349 +f 346 347 351 350 +f 347 348 352 351 +f 348 345 349 352 +f 345 348 347 346 +f 349 350 351 352 +g Mesh045 +usemtl Blue +f 353 354 358 357 +f 354 355 359 358 +f 355 356 360 359 +f 356 353 357 360 +f 353 356 355 354 +f 357 358 359 360 +g Mesh046 +usemtl Red +f 361 362 366 365 +f 362 363 367 366 +f 363 364 368 367 +f 364 361 365 368 +f 361 364 363 362 +f 365 366 367 368 +g Mesh047 +usemtl Green +f 369 370 374 373 +f 370 371 375 374 +f 371 372 376 375 +f 372 369 373 376 +f 369 372 371 370 +f 373 374 375 376 +g Mesh048 +usemtl Blue +f 377 378 382 381 +f 378 379 383 382 +f 379 380 384 383 +f 380 377 381 384 +f 377 380 379 378 +f 381 382 383 384 +g Mesh049 +usemtl Red +f 385 386 390 389 +f 386 387 391 390 +f 387 388 392 391 +f 388 385 389 392 +f 385 388 387 386 +f 389 390 391 392 +g Mesh050 +usemtl Green +f 393 394 398 397 +f 394 395 399 398 +f 395 396 400 399 +f 396 393 397 400 +f 393 396 395 394 +f 397 398 399 400 +g Mesh051 +usemtl Blue +f 401 402 406 405 +f 402 403 407 406 +f 403 404 408 407 +f 404 401 405 408 +f 401 404 403 402 +f 405 406 407 408 +g Mesh052 +usemtl Red +f 409 410 414 413 +f 410 411 415 414 +f 411 412 416 415 +f 412 409 413 416 +f 409 412 411 410 +f 413 414 415 416 +g Mesh053 +usemtl Green +f 417 418 422 421 +f 418 419 423 422 +f 419 420 424 423 +f 420 417 421 424 +f 417 420 419 418 +f 421 422 423 424 +g Mesh054 +usemtl Blue +f 425 426 430 429 +f 426 427 431 430 +f 427 428 432 431 +f 428 425 429 432 +f 425 428 427 426 +f 429 430 431 432 +g Mesh055 +usemtl Red +f 433 434 438 437 +f 434 435 439 438 +f 435 436 440 439 +f 436 433 437 440 +f 433 436 435 434 +f 437 438 439 440 +g Mesh056 +usemtl Green +f 441 442 446 445 +f 442 443 447 446 +f 443 444 448 447 +f 444 441 445 448 +f 441 444 443 442 +f 445 446 447 448 +g Mesh057 +usemtl Blue +f 449 450 454 453 +f 450 451 455 454 +f 451 452 456 455 +f 452 449 453 456 +f 449 452 451 450 +f 453 454 455 456 +g Mesh058 +usemtl Red +f 457 458 462 461 +f 458 459 463 462 +f 459 460 464 463 +f 460 457 461 464 +f 457 460 459 458 +f 461 462 463 464 +g Mesh059 +usemtl Green +f 465 466 470 469 +f 466 467 471 470 +f 467 468 472 471 +f 468 465 469 472 +f 465 468 467 466 +f 469 470 471 472 +g Mesh060 +usemtl Blue +f 473 474 478 477 +f 474 475 479 478 +f 475 476 480 479 +f 476 473 477 480 +f 473 476 475 474 +f 477 478 479 480 +g Mesh061 +usemtl Red +f 481 482 486 485 +f 482 483 487 486 +f 483 484 488 487 +f 484 481 485 488 +f 481 484 483 482 +f 485 486 487 488 +g Mesh062 +usemtl Green +f 489 490 494 493 +f 490 491 495 494 +f 491 492 496 495 +f 492 489 493 496 +f 489 492 491 490 +f 493 494 495 496 +g Mesh063 +usemtl Blue +f 497 498 502 501 +f 498 499 503 502 +f 499 500 504 503 +f 500 497 501 504 +f 497 500 499 498 +f 501 502 503 504 +g Mesh064 +usemtl Red +f 505 506 510 509 +f 506 507 511 510 +f 507 508 512 511 +f 508 505 509 512 +f 505 508 507 506 +f 509 510 511 512 +g Mesh065 +usemtl Green +f 513 514 518 517 +f 514 515 519 518 +f 515 516 520 519 +f 516 513 517 520 +f 513 516 515 514 +f 517 518 519 520 +g Mesh066 +usemtl Blue +f 521 522 526 525 +f 522 523 527 526 +f 523 524 528 527 +f 524 521 525 528 +f 521 524 523 522 +f 525 526 527 528 +g Mesh067 +usemtl Red +f 529 530 534 533 +f 530 531 535 534 +f 531 532 536 535 +f 532 529 533 536 +f 529 532 531 530 +f 533 534 535 536 +g Mesh068 +usemtl Green +f 537 538 542 541 +f 538 539 543 542 +f 539 540 544 543 +f 540 537 541 544 +f 537 540 539 538 +f 541 542 543 544 +g Mesh069 +usemtl Blue +f 545 546 550 549 +f 546 547 551 550 +f 547 548 552 551 +f 548 545 549 552 +f 545 548 547 546 +f 549 550 551 552 +g Mesh070 +usemtl Red +f 553 554 558 557 +f 554 555 559 558 +f 555 556 560 559 +f 556 553 557 560 +f 553 556 555 554 +f 557 558 559 560 +g Mesh071 +usemtl Green +f 561 562 566 565 +f 562 563 567 566 +f 563 564 568 567 +f 564 561 565 568 +f 561 564 563 562 +f 565 566 567 568 +g Mesh072 +usemtl Blue +f 569 570 574 573 +f 570 571 575 574 +f 571 572 576 575 +f 572 569 573 576 +f 569 572 571 570 +f 573 574 575 576 +g Mesh073 +usemtl Red +f 577 578 582 581 +f 578 579 583 582 +f 579 580 584 583 +f 580 577 581 584 +f 577 580 579 578 +f 581 582 583 584 +g Mesh074 +usemtl Green +f 585 586 590 589 +f 586 587 591 590 +f 587 588 592 591 +f 588 585 589 592 +f 585 588 587 586 +f 589 590 591 592 +g Mesh075 +usemtl Blue +f 593 594 598 597 +f 594 595 599 598 +f 595 596 600 599 +f 596 593 597 600 +f 593 596 595 594 +f 597 598 599 600 +g Mesh076 +usemtl Red +f 601 602 606 605 +f 602 603 607 606 +f 603 604 608 607 +f 604 601 605 608 +f 601 604 603 602 +f 605 606 607 608 +g Mesh077 +usemtl Green +f 609 610 614 613 +f 610 611 615 614 +f 611 612 616 615 +f 612 609 613 616 +f 609 612 611 610 +f 613 614 615 616 +g Mesh078 +usemtl Blue +f 617 618 622 621 +f 618 619 623 622 +f 619 620 624 623 +f 620 617 621 624 +f 617 620 619 618 +f 621 622 623 624 +g Mesh079 +usemtl Red +f 625 626 630 629 +f 626 627 631 630 +f 627 628 632 631 +f 628 625 629 632 +f 625 628 627 626 +f 629 630 631 632 +g Mesh080 +usemtl Green +f 633 634 638 637 +f 634 635 639 638 +f 635 636 640 639 +f 636 633 637 640 +f 633 636 635 634 +f 637 638 639 640 +g Mesh081 +usemtl Blue +f 641 642 646 645 +f 642 643 647 646 +f 643 644 648 647 +f 644 641 645 648 +f 641 644 643 642 +f 645 646 647 648 +g Mesh082 +usemtl Red +f 649 650 654 653 +f 650 651 655 654 +f 651 652 656 655 +f 652 649 653 656 +f 649 652 651 650 +f 653 654 655 656 +g Mesh083 +usemtl Green +f 657 658 662 661 +f 658 659 663 662 +f 659 660 664 663 +f 660 657 661 664 +f 657 660 659 658 +f 661 662 663 664 +g Mesh084 +usemtl Blue +f 665 666 670 669 +f 666 667 671 670 +f 667 668 672 671 +f 668 665 669 672 +f 665 668 667 666 +f 669 670 671 672 +g Mesh085 +usemtl Red +f 673 674 678 677 +f 674 675 679 678 +f 675 676 680 679 +f 676 673 677 680 +f 673 676 675 674 +f 677 678 679 680 +g Mesh086 +usemtl Green +f 681 682 686 685 +f 682 683 687 686 +f 683 684 688 687 +f 684 681 685 688 +f 681 684 683 682 +f 685 686 687 688 +g Mesh087 +usemtl Blue +f 689 690 694 693 +f 690 691 695 694 +f 691 692 696 695 +f 692 689 693 696 +f 689 692 691 690 +f 693 694 695 696 +g Mesh088 +usemtl Red +f 697 698 702 701 +f 698 699 703 702 +f 699 700 704 703 +f 700 697 701 704 +f 697 700 699 698 +f 701 702 703 704 +g Mesh089 +usemtl Green +f 705 706 710 709 +f 706 707 711 710 +f 707 708 712 711 +f 708 705 709 712 +f 705 708 707 706 +f 709 710 711 712 +g Mesh090 +usemtl Blue +f 713 714 718 717 +f 714 715 719 718 +f 715 716 720 719 +f 716 713 717 720 +f 713 716 715 714 +f 717 718 719 720 +g Mesh091 +usemtl Red +f 721 722 726 725 +f 722 723 727 726 +f 723 724 728 727 +f 724 721 725 728 +f 721 724 723 722 +f 725 726 727 728 +g Mesh092 +usemtl Green +f 729 730 734 733 +f 730 731 735 734 +f 731 732 736 735 +f 732 729 733 736 +f 729 732 731 730 +f 733 734 735 736 +g Mesh093 +usemtl Blue +f 737 738 742 741 +f 738 739 743 742 +f 739 740 744 743 +f 740 737 741 744 +f 737 740 739 738 +f 741 742 743 744 +g Mesh094 +usemtl Red +f 745 746 750 749 +f 746 747 751 750 +f 747 748 752 751 +f 748 745 749 752 +f 745 748 747 746 +f 749 750 751 752 +g Mesh095 +usemtl Green +f 753 754 758 757 +f 754 755 759 758 +f 755 756 760 759 +f 756 753 757 760 +f 753 756 755 754 +f 757 758 759 760 +g Mesh096 +usemtl Blue +f 761 762 766 765 +f 762 763 767 766 +f 763 764 768 767 +f 764 761 765 768 +f 761 764 763 762 +f 765 766 767 768 +g Mesh097 +usemtl Red +f 769 770 774 773 +f 770 771 775 774 +f 771 772 776 775 +f 772 769 773 776 +f 769 772 771 770 +f 773 774 775 776 +g Mesh098 +usemtl Green +f 777 778 782 781 +f 778 779 783 782 +f 779 780 784 783 +f 780 777 781 784 +f 777 780 779 778 +f 781 782 783 784 +g Mesh099 +usemtl Blue +f 785 786 790 789 +f 786 787 791 790 +f 787 788 792 791 +f 788 785 789 792 +f 785 788 787 786 +f 789 790 791 792 +g Mesh100 +usemtl Red +f 793 794 798 797 +f 794 795 799 798 +f 795 796 800 799 +f 796 793 797 800 +f 793 796 795 794 +f 797 798 799 800 diff --git a/test/testfiles/obj/icosahedron.obj b/test/testfiles/obj/icosahedron.obj new file mode 100644 index 0000000..46835db --- /dev/null +++ b/test/testfiles/obj/icosahedron.obj @@ -0,0 +1,61 @@ +newmtl Blue +Ka 0.500000 0.500000 0.500000 +Kd 0.000000 0.541180 0.721570 +Ks 0.600000 0.600000 0.600000 + +usemtl Blue +g Icosahedron + +v 0 0.5257311121191336 0.85065080835204 +v 0 0.5257311121191336 -0.85065080835204 +v 0 -0.5257311121191336 0.85065080835204 +v 0 -0.5257311121191336 -0.85065080835204 +v 0.5257311121191336 0.85065080835204 0 +v 0.5257311121191336 -0.85065080835204 0 +v -0.5257311121191336 0.85065080835204 0 +v -0.5257311121191336 -0.85065080835204 0 +v 0.85065080835204 0 0.5257311121191336 +v 0.85065080835204 0 -0.5257311121191336 +v -0.85065080835204 0 0.5257311121191336 +v -0.85065080835204 0 -0.5257311121191336 +vn 0.3568220897730902 0 0.9341723589627158 +vn 0 0.9341723589627158 0.3568220897730902 +vn -0.5773502691896258 0.5773502691896258 0.5773502691896257 +vn 0.5773502691896258 0.5773502691896257 0.5773502691896258 +vn -0.3568220897730902 0 0.9341723589627158 +vn -0.3568220897730902 0 -0.9341723589627158 +vn 0.5773502691896258 0.5773502691896258 -0.5773502691896257 +vn 0 0.9341723589627158 -0.3568220897730902 +vn 0.3568220897730902 0 -0.9341723589627158 +vn -0.5773502691896258 0.5773502691896257 -0.5773502691896258 +vn 0.5773502691896258 -0.5773502691896258 0.5773502691896257 +vn 0 -0.9341723589627158 0.3568220897730902 +vn -0.5773502691896258 -0.5773502691896257 0.5773502691896258 +vn 0 -0.9341723589627158 -0.3568220897730902 +vn -0.5773502691896258 -0.5773502691896258 -0.5773502691896257 +vn 0.5773502691896258 -0.5773502691896257 -0.5773502691896258 +vn 0.9341723589627158 0.3568220897730902 0 +vn 0.9341723589627158 -0.3568220897730902 0 +vn -0.9341723589627158 0.3568220897730902 0 +vn -0.9341723589627158 -0.3568220897730902 0 + +f 1//1 3//1 9//1 +f 1//2 5//2 7//2 +f 1//3 7//3 11//3 +f 1//4 9//4 5//4 +f 1//5 11//5 3//5 +f 2//6 4//6 12//6 +f 2//7 5//7 10//7 +f 2//8 7//8 5//8 +f 2//9 10//9 4//9 +f 2//10 12//10 7//10 +f 3//11 6//11 9//11 +f 3//12 8//12 6//12 +f 3//13 11//13 8//13 +f 4//14 6//14 8//14 +f 4//15 8//15 12//15 +f 4//16 10//16 6//16 +f 5//17 9//17 10//17 +f 6//18 10//18 9//18 +f 7//19 12//19 11//19 +f 8//20 11//20 12//20 diff --git a/test/testfiles/obj/materials.mtl b/test/testfiles/obj/materials.mtl new file mode 100644 index 0000000..cef30a7 --- /dev/null +++ b/test/testfiles/obj/materials.mtl @@ -0,0 +1,9 @@ +newmtl MaterialName1 +Ka 0.500000 0.500000 0.500000 +Kd 1.000000 0.000000 0.000000 +Ks 0.600000 0.600000 0.600000 + +newmtl MaterialName2 +Ka 0.500000 0.500000 0.500000 +Kd 0.000000 1.000000 0.000000 +Ks 0.600000 0.600000 0.600000 diff --git a/test/testfiles/obj/single_rectangle.obj b/test/testfiles/obj/single_rectangle.obj new file mode 100644 index 0000000..6447f85 --- /dev/null +++ b/test/testfiles/obj/single_rectangle.obj @@ -0,0 +1,5 @@ +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +v 0.0 1.0 0.0 +f 1 2 3 4 diff --git a/test/testfiles/obj/single_triangle.obj b/test/testfiles/obj/single_triangle.obj new file mode 100644 index 0000000..0cabc1d --- /dev/null +++ b/test/testfiles/obj/single_triangle.obj @@ -0,0 +1,4 @@ +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +f 1 2 3 diff --git a/test/testfiles/obj/single_triangle_in_mesh.obj b/test/testfiles/obj/single_triangle_in_mesh.obj new file mode 100644 index 0000000..16f05ee --- /dev/null +++ b/test/testfiles/obj/single_triangle_in_mesh.obj @@ -0,0 +1,5 @@ +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +g MeshName +f 1 2 3 diff --git a/test/testfiles/obj/single_triangle_negative_indices.obj b/test/testfiles/obj/single_triangle_negative_indices.obj new file mode 100644 index 0000000..c227a76 --- /dev/null +++ b/test/testfiles/obj/single_triangle_negative_indices.obj @@ -0,0 +1,4 @@ +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +f -3 -2 -1 diff --git a/test/testfiles/obj/single_triangle_with_comments.obj b/test/testfiles/obj/single_triangle_with_comments.obj new file mode 100644 index 0000000..532d5b1 --- /dev/null +++ b/test/testfiles/obj/single_triangle_with_comments.obj @@ -0,0 +1,15 @@ +# this is a comment +# this is another comment +g MyMeshName # mesh comment +v 0.0 0.0 0.0 # comment at the end of line + # indented comment +v 1.0 0.0 0.0 # multiple spaces +# comment +v 1.0 1.0 0.0 # comment + +# comment +f 1 2 3 + +# comment +# comment a the end of the file +# last comment diff --git a/test/testfiles/obj/single_triangle_with_material.obj b/test/testfiles/obj/single_triangle_with_material.obj new file mode 100644 index 0000000..28df0d2 --- /dev/null +++ b/test/testfiles/obj/single_triangle_with_material.obj @@ -0,0 +1,9 @@ +mtllib materials.mtl + +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 + +g MeshName +usemtl MaterialName1 +f 1 2 3 diff --git a/test/testfiles/obj/single_triangle_with_normals_and_uvs.obj b/test/testfiles/obj/single_triangle_with_normals_and_uvs.obj new file mode 100644 index 0000000..f0fcdbf --- /dev/null +++ b/test/testfiles/obj/single_triangle_with_normals_and_uvs.obj @@ -0,0 +1,8 @@ +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +vn 0.0 0.0 1.0 +vt 0.0 0.0 +vt 1.0 0.0 +vt 1.0 1.0 +f 1/1/1 2/2/1 3/3/1 diff --git a/test/testfiles/obj/two_meshes_same_vertices.obj b/test/testfiles/obj/two_meshes_same_vertices.obj new file mode 100644 index 0000000..669bc7f --- /dev/null +++ b/test/testfiles/obj/two_meshes_same_vertices.obj @@ -0,0 +1,10 @@ +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +v 0.0 0.0 1.0 + +g MeshName1 +f 1 2 3 + +g MeshName2 +f 1 2 4 diff --git a/test/testfiles/obj/two_meshes_with_same_name.obj b/test/testfiles/obj/two_meshes_with_same_name.obj new file mode 100644 index 0000000..c6c8216 --- /dev/null +++ b/test/testfiles/obj/two_meshes_with_same_name.obj @@ -0,0 +1,10 @@ +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +v 0.0 0.0 1.0 + +g MeshName +f 1 2 3 + +g MeshName +f 1 2 4 diff --git a/test/testfiles/obj/two_triangles.obj b/test/testfiles/obj/two_triangles.obj new file mode 100644 index 0000000..2bfc52e --- /dev/null +++ b/test/testfiles/obj/two_triangles.obj @@ -0,0 +1,10 @@ +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 + +v 0.0 0.0 1.0 +v 1.0 0.0 1.0 +v 1.0 1.0 1.0 + +f 1 2 3 +f 4 5 6 diff --git a/test/testfiles/obj/two_triangles_in_meshes.obj b/test/testfiles/obj/two_triangles_in_meshes.obj new file mode 100644 index 0000000..f26a3c9 --- /dev/null +++ b/test/testfiles/obj/two_triangles_in_meshes.obj @@ -0,0 +1,12 @@ +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +v 0.0 0.0 1.0 +v 1.0 0.0 1.0 +v 1.0 1.0 1.0 + +g MeshName1 +f 1 2 3 + +g MeshName2 +f 4 5 6 diff --git a/test/testfiles/obj/two_triangles_with_material.obj b/test/testfiles/obj/two_triangles_with_material.obj new file mode 100644 index 0000000..50612d8 --- /dev/null +++ b/test/testfiles/obj/two_triangles_with_material.obj @@ -0,0 +1,14 @@ +mtllib materials.mtl + +v 0.0 0.0 0.0 +v 1.0 0.0 0.0 +v 1.0 1.0 0.0 +v 0.0 0.0 1.0 +v 1.0 0.0 1.0 +v 1.0 1.0 1.0 + +g MeshName +usemtl MaterialName1 +f 1 2 3 +usemtl MaterialName2 +f 4 5 6 diff --git a/test/testfiles/off/cube.off b/test/testfiles/off/cube.off new file mode 100644 index 0000000..196cbfc --- /dev/null +++ b/test/testfiles/off/cube.off @@ -0,0 +1,19 @@ +OFF + +8 6 0 + +0.0 0.0 0.0 +1.0 0.0 0.0 +1.0 1.0 0.0 +0.0 1.0 0.0 +0.0 0.0 1.0 +1.0 0.0 1.0 +1.0 1.0 1.0 +0.0 1.0 1.0 + +4 0 1 5 4 +4 1 2 6 5 +4 2 3 7 6 +4 3 0 4 7 +4 0 3 2 1 +4 4 5 6 7 diff --git a/test/testfiles/off/single_rectangle.off b/test/testfiles/off/single_rectangle.off new file mode 100644 index 0000000..03f17e5 --- /dev/null +++ b/test/testfiles/off/single_rectangle.off @@ -0,0 +1,10 @@ +OFF + +4 1 + +0.0 0.0 0.0 +1.0 0.0 0.0 +1.0 1.0 0.0 +0.0 1.0 0.0 + +4 0 1 2 3 diff --git a/test/testfiles/off/single_triangle.off b/test/testfiles/off/single_triangle.off new file mode 100644 index 0000000..e13ce24 --- /dev/null +++ b/test/testfiles/off/single_triangle.off @@ -0,0 +1,6 @@ +OFF +3 1 0 +0.0 0.0 0.0 +1.0 0.0 0.0 +1.0 1.0 0.0 +3 0 1 2 diff --git a/test/testfiles/off/single_triangle_with_comments.off b/test/testfiles/off/single_triangle_with_comments.off new file mode 100644 index 0000000..36b6413 --- /dev/null +++ b/test/testfiles/off/single_triangle_with_comments.off @@ -0,0 +1,20 @@ +# this is a comment +OFF +# this is another comment +3 1 12 # comment + +# more comments + +0.0 0.0 0.0 # comment at the end of line + # indented comment + +1.0 0.0 0.0 # another end of line comment +# comment +1.0 1.0 0.0 # comment + +# comment +3 0 1 2 + +# comment +# comment a the end of the file +# last comment diff --git a/test/testfiles/off/two_triangles.off b/test/testfiles/off/two_triangles.off new file mode 100644 index 0000000..afa4ed9 --- /dev/null +++ b/test/testfiles/off/two_triangles.off @@ -0,0 +1,14 @@ +OFF + +6 2 + +0.0 0.0 0.0 +1.0 0.0 0.0 +1.0 1.0 0.0 + +0.0 0.0 1.0 +1.0 0.0 1.0 +1.0 1.0 1.0 + +3 0 1 2 +3 3 4 5 diff --git a/test/testfiles/ply/cube.ply b/test/testfiles/ply/cube.ply new file mode 100644 index 0000000..04e8e7d --- /dev/null +++ b/test/testfiles/ply/cube.ply @@ -0,0 +1,25 @@ +ply +format ascii 1.0 +element vertex 8 +property float x +property float y +property float z +element face 6 +property list uchar int vertex_index +end_header + +0.0 0.0 0.0 +1.0 0.0 0.0 +1.0 1.0 0.0 +0.0 1.0 0.0 +0.0 0.0 1.0 +1.0 0.0 1.0 +1.0 1.0 1.0 +0.0 1.0 1.0 + +4 0 1 5 4 +4 1 2 6 5 +4 2 3 7 6 +4 3 0 4 7 +4 0 3 2 1 +4 4 5 6 7 diff --git a/test/testfiles/ply/cube_meshlab_ascii.ply b/test/testfiles/ply/cube_meshlab_ascii.ply new file mode 100644 index 0000000..147d26b --- /dev/null +++ b/test/testfiles/ply/cube_meshlab_ascii.ply @@ -0,0 +1,30 @@ +ply +format ascii 1.0 +comment VCGLIB generated +element vertex 8 +property float x +property float y +property float z +element face 12 +property list uchar int vertex_indices +end_header +0 0 0 +1 0 0 +1 1 0 +0 1 0 +0 0 1 +1 0 1 +1 1 1 +0 1 1 +3 0 1 5 +3 0 5 4 +3 1 2 6 +3 1 6 5 +3 2 3 7 +3 2 7 6 +3 3 0 4 +3 3 4 7 +3 0 3 2 +3 0 2 1 +3 4 5 6 +3 4 6 7 diff --git a/test/testfiles/ply/cube_meshlab_binary.ply b/test/testfiles/ply/cube_meshlab_binary.ply new file mode 100644 index 0000000..c3b2bd7 Binary files /dev/null and b/test/testfiles/ply/cube_meshlab_binary.ply differ diff --git a/test/testfiles/ply/cube_rgb.ply b/test/testfiles/ply/cube_rgb.ply new file mode 100644 index 0000000..09933b1 --- /dev/null +++ b/test/testfiles/ply/cube_rgb.ply @@ -0,0 +1,35 @@ +ply +format ascii 1.0 +comment VCGLIB generated +element vertex 8 +property float x +property float y +property float z +element face 12 +property list uchar int vertex_indices +property list uchar float texcoord +property uchar red +property uchar green +property uchar blue +property uchar alpha +end_header +0 0 0 +1 0 0 +1 1 0 +0 1 0 +0 0 1 +1 0 1 +1 1 1 +0 1 1 +3 0 1 5 6 0.000000 0.000000 1.000000 0.000000 1.000000 1.000000 204 0 0 255 +3 0 5 4 6 0.000000 0.000000 1.000000 1.000000 0.000000 1.000000 204 0 0 255 +3 1 2 6 6 0.000000 0.000000 1.000000 0.000000 1.000000 1.000000 0 204 0 255 +3 1 6 5 6 0.000000 0.000000 1.000000 1.000000 0.000000 1.000000 0 204 0 255 +3 2 3 7 6 0.000000 0.000000 1.000000 0.000000 1.000000 1.000000 0 0 204 255 +3 2 7 6 6 0.000000 0.000000 1.000000 1.000000 0.000000 1.000000 0 0 204 255 +3 3 0 4 6 0.000000 0.000000 1.000000 0.000000 1.000000 1.000000 204 204 204 255 +3 3 4 7 6 0.000000 0.000000 1.000000 1.000000 0.000000 1.000000 204 204 204 255 +3 0 3 2 6 0.000000 0.000000 1.000000 0.000000 1.000000 1.000000 204 204 204 255 +3 0 2 1 6 0.000000 0.000000 1.000000 1.000000 0.000000 1.000000 204 204 204 255 +3 4 5 6 6 0.000000 0.000000 1.000000 0.000000 1.000000 1.000000 204 204 204 255 +3 4 6 7 6 0.000000 0.000000 1.000000 1.000000 0.000000 1.000000 204 204 204 255 diff --git a/test/testfiles/ply/cube_rgb_binary.ply b/test/testfiles/ply/cube_rgb_binary.ply new file mode 100644 index 0000000..cfa6cd9 Binary files /dev/null and b/test/testfiles/ply/cube_rgb_binary.ply differ diff --git a/test/testfiles/ply/single_rectangle.ply b/test/testfiles/ply/single_rectangle.ply new file mode 100644 index 0000000..88a4738 --- /dev/null +++ b/test/testfiles/ply/single_rectangle.ply @@ -0,0 +1,16 @@ +ply +format ascii 1.0 +element vertex 4 +property float x +property float y +property float z +element face 1 +property list uchar int vertex_index +end_header + +0.0 0.0 0.0 +1.0 0.0 0.0 +1.0 1.0 0.0 +0.0 1.0 0.0 + +4 0 1 2 3 diff --git a/test/testfiles/ply/single_triangle.ply b/test/testfiles/ply/single_triangle.ply new file mode 100644 index 0000000..b8dfc57 --- /dev/null +++ b/test/testfiles/ply/single_triangle.ply @@ -0,0 +1,13 @@ +ply +format ascii 1.0 +element vertex 3 +property float x +property float y +property float z +element face 1 +property list uchar int vertex_index +end_header +0.0 0.0 0.0 +1.0 0.0 0.0 +1.0 1.0 0.0 +3 0 1 2 diff --git a/test/testfiles/ply/single_triangle_with_comments.ply b/test/testfiles/ply/single_triangle_with_comments.ply new file mode 100644 index 0000000..415a738 --- /dev/null +++ b/test/testfiles/ply/single_triangle_with_comments.ply @@ -0,0 +1,18 @@ +ply +format ascii 1.0 +comment this is a comment +element vertex 3 +property float x +property float y +property float z +element face 1 +property list uchar int vertex_index +end_header + +0.0 0.0 0.0 +comment this is another comment +comment and another + +1.0 0.0 0.0 +1.0 1.0 0.0 +3 0 1 2 diff --git a/test/testfiles/ply/two_triangles.ply b/test/testfiles/ply/two_triangles.ply new file mode 100644 index 0000000..b904f13 --- /dev/null +++ b/test/testfiles/ply/two_triangles.ply @@ -0,0 +1,20 @@ +ply +format ascii 1.0 +element vertex 6 +property float x +property float y +property float z +element face 2 +property list uchar int vertex_index +end_header + +0.0 0.0 0.0 +1.0 0.0 0.0 +1.0 1.0 0.0 + +0.0 0.0 1.0 +1.0 0.0 1.0 +1.0 1.0 1.0 + +3 0 1 2 +3 3 4 5 diff --git a/test/testfiles/stl/cube_meshlab_ascii.stl b/test/testfiles/stl/cube_meshlab_ascii.stl new file mode 100644 index 0000000..7c401c7 --- /dev/null +++ b/test/testfiles/stl/cube_meshlab_ascii.stl @@ -0,0 +1,86 @@ +solid STL generated by MeshLab + facet normal 0.000000e+00 -1.000000e+00 0.000000e+00 + outer loop + vertex 0.000000e+00 0.000000e+00 0.000000e+00 + vertex 1.000000e+00 0.000000e+00 0.000000e+00 + vertex 1.000000e+00 0.000000e+00 1.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 -1.000000e+00 0.000000e+00 + outer loop + vertex 0.000000e+00 0.000000e+00 0.000000e+00 + vertex 1.000000e+00 0.000000e+00 1.000000e+00 + vertex 0.000000e+00 0.000000e+00 1.000000e+00 + endloop + endfacet + facet normal 1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex 1.000000e+00 0.000000e+00 0.000000e+00 + vertex 1.000000e+00 1.000000e+00 0.000000e+00 + vertex 1.000000e+00 1.000000e+00 1.000000e+00 + endloop + endfacet + facet normal 1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex 1.000000e+00 0.000000e+00 0.000000e+00 + vertex 1.000000e+00 1.000000e+00 1.000000e+00 + vertex 1.000000e+00 0.000000e+00 1.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 1.000000e+00 0.000000e+00 + outer loop + vertex 1.000000e+00 1.000000e+00 0.000000e+00 + vertex 0.000000e+00 1.000000e+00 0.000000e+00 + vertex 0.000000e+00 1.000000e+00 1.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 1.000000e+00 -0.000000e+00 + outer loop + vertex 1.000000e+00 1.000000e+00 0.000000e+00 + vertex 0.000000e+00 1.000000e+00 1.000000e+00 + vertex 1.000000e+00 1.000000e+00 1.000000e+00 + endloop + endfacet + facet normal -1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex 0.000000e+00 1.000000e+00 0.000000e+00 + vertex 0.000000e+00 0.000000e+00 0.000000e+00 + vertex 0.000000e+00 0.000000e+00 1.000000e+00 + endloop + endfacet + facet normal -1.000000e+00 0.000000e+00 0.000000e+00 + outer loop + vertex 0.000000e+00 1.000000e+00 0.000000e+00 + vertex 0.000000e+00 0.000000e+00 1.000000e+00 + vertex 0.000000e+00 1.000000e+00 1.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 0.000000e+00 -1.000000e+00 + outer loop + vertex 0.000000e+00 0.000000e+00 0.000000e+00 + vertex 0.000000e+00 1.000000e+00 0.000000e+00 + vertex 1.000000e+00 1.000000e+00 0.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 0.000000e+00 -1.000000e+00 + outer loop + vertex 0.000000e+00 0.000000e+00 0.000000e+00 + vertex 1.000000e+00 1.000000e+00 0.000000e+00 + vertex 1.000000e+00 0.000000e+00 0.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 0.000000e+00 1.000000e+00 + outer loop + vertex 0.000000e+00 0.000000e+00 1.000000e+00 + vertex 1.000000e+00 0.000000e+00 1.000000e+00 + vertex 1.000000e+00 1.000000e+00 1.000000e+00 + endloop + endfacet + facet normal 0.000000e+00 0.000000e+00 1.000000e+00 + outer loop + vertex 0.000000e+00 0.000000e+00 1.000000e+00 + vertex 1.000000e+00 1.000000e+00 1.000000e+00 + vertex 0.000000e+00 1.000000e+00 1.000000e+00 + endloop + endfacet +endsolid vcg diff --git a/test/testfiles/stl/cube_meshlab_binary.stl b/test/testfiles/stl/cube_meshlab_binary.stl new file mode 100644 index 0000000..9512df8 Binary files /dev/null and b/test/testfiles/stl/cube_meshlab_binary.stl differ diff --git a/test/testfiles/stl/single_triangle.stl b/test/testfiles/stl/single_triangle.stl new file mode 100644 index 0000000..9aec65c --- /dev/null +++ b/test/testfiles/stl/single_triangle.stl @@ -0,0 +1,9 @@ +solid MeshName +facet normal 0.0 0.0 1.0 + outer loop + vertex 0.0 0.0 0.0 + vertex 1.0 0.0 0.0 + vertex 1.0 1.0 0.0 + endloop +endfacet +endsolid MeshName diff --git a/test/testfiles/stl/single_triangle_no_normal.stl b/test/testfiles/stl/single_triangle_no_normal.stl new file mode 100644 index 0000000..0164b69 --- /dev/null +++ b/test/testfiles/stl/single_triangle_no_normal.stl @@ -0,0 +1,9 @@ +solid MeshName +facet + outer loop + vertex 0.0 0.0 0.0 + vertex 1.0 0.0 0.0 + vertex 1.0 1.0 0.0 + endloop +endfacet +endsolid MeshName diff --git a/test/testfiles/stl/single_triangle_with_comments.stl b/test/testfiles/stl/single_triangle_with_comments.stl new file mode 100644 index 0000000..33e4c4a --- /dev/null +++ b/test/testfiles/stl/single_triangle_with_comments.stl @@ -0,0 +1,19 @@ +# comment +solid MeshName # comment + # comment + # comment + # comment +facet normal 0.0 0.0 1.0 # comment + # comment + outer loop # comment + # comment + vertex 0.0 0.0 0.0 # comment + vertex 1.0 0.0 0.0 # comment + vertex 1.0 1.0 0.0 # comment + endloop # comment + # comment +endfacet # comment + # comment +endsolid MeshName # comment + # comment + \ No newline at end of file diff --git a/test/testfiles/stl/stl_ascii.stl b/test/testfiles/stl/stl_ascii.stl new file mode 100644 index 0000000..9441588 --- /dev/null +++ b/test/testfiles/stl/stl_ascii.stl @@ -0,0 +1,4006 @@ +solid Untitled-5427e5af +facet normal 0.0 0.0 -1.0 + outer loop + vertex 1.17 1.63 0.0 + vertex 0.0 0.0 0.0 + vertex 0.0 1.63 0.0 + endloop +endfacet +facet normal 0.0 0.0 -1.0 + outer loop + vertex 0.0 0.0 0.0 + vertex 1.17 1.63 0.0 + vertex 1.17 0.0 0.0 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 1.17 0.0 1.2 + vertex 0.0 1.63 1.2 + vertex 0.0 0.0 1.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 0.0 1.63 1.2 + vertex 1.17 0.0 1.2 + vertex 1.17 1.63 1.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 0.0 1.63 1.2 + vertex 0.0 0.0 0.0 + vertex 0.0 0.0 1.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 0.0 0.0 0.0 + vertex 0.0 1.63 1.2 + vertex 0.0 1.63 0.0 + endloop +endfacet +facet normal -0.0 1.0 0.0 + outer loop + vertex 0.0 1.63 1.2 + vertex 1.17 1.63 0.0 + vertex 0.0 1.63 0.0 + endloop +endfacet +facet normal -0.0 1.0 0.0 + outer loop + vertex 1.17 1.63 0.0 + vertex 0.0 1.63 1.2 + vertex 1.17 1.63 1.2 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 1.17 1.63 0.0 + vertex 1.17 0.0 1.2 + vertex 1.17 0.0 0.0 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 1.17 0.0 1.2 + vertex 1.17 1.63 0.0 + vertex 1.17 1.63 1.2 + endloop +endfacet +facet normal -0.0 -1.0 -0.0 + outer loop + vertex 1.17 0.0 1.2 + vertex 0.0 0.0 0.0 + vertex 1.17 0.0 0.0 + endloop +endfacet +facet normal -0.0 -1.0 -0.0 + outer loop + vertex 0.0 0.0 0.0 + vertex 1.17 0.0 1.2 + vertex 0.0 0.0 1.2 + endloop +endfacet +facet normal 0.0 0.0 -1.0 + outer loop + vertex 1.73120970770261 3.31831671830375 0.0 + vertex 1.69335580395681 1.58667960794511 0.0 + vertex 1.21240217992756 2.46342564388318 0.0 + endloop +endfacet +facet normal 0.0 0.0 -1.0 + outer loop + vertex 1.69335580395681 1.58667960794511 0.0 + vertex 1.73120970770261 3.31831671830375 0.0 + vertex 2.69311695576112 1.5648246464276 0.0 + endloop +endfacet +facet normal 0.0 0.0 -1.0 + outer loop + vertex 2.69311695576112 1.5648246464276 0.0 + vertex 1.73120970770261 3.31831671830375 0.0 + vertex 2.73097085950691 3.29646175678623 0.0 + endloop +endfacet +facet normal 0.0 0.0 -1.0 + outer loop + vertex 2.69311695576112 1.5648246464276 0.0 + vertex 2.73097085950691 3.29646175678623 0.0 + vertex 3.21192448353617 2.41971572084816 0.0 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 1.69335580395681 1.58667960794511 0.54 + vertex 1.73120970770261 3.31831671830375 0.54 + vertex 1.21240217992756 2.46342564388318 0.54 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 1.73120970770261 3.31831671830375 0.54 + vertex 1.69335580395681 1.58667960794511 0.54 + vertex 2.69311695576112 1.5648246464276 0.54 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 1.73120970770261 3.31831671830375 0.54 + vertex 2.69311695576112 1.5648246464276 0.54 + vertex 2.73097085950691 3.29646175678623 0.54 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 2.73097085950691 3.29646175678623 0.54 + vertex 2.69311695576112 1.5648246464276 0.54 + vertex 3.21192448353617 2.41971572084816 0.54 + endloop +endfacet +facet normal -0.854891074420561 0.518807527775049 0.0 + outer loop + vertex 1.73120970770261 3.31831671830375 0.54 + vertex 1.21240217992756 2.46342564388318 0.0 + vertex 1.21240217992756 2.46342564388318 0.54 + endloop +endfacet +facet normal -0.854891074420561 0.518807527775049 0.0 + outer loop + vertex 1.21240217992756 2.46342564388318 0.0 + vertex 1.73120970770261 3.31831671830375 0.54 + vertex 1.73120970770261 3.31831671830375 0.0 + endloop +endfacet +facet normal 0.0218549615175129 0.999761151804304 0.0 + outer loop + vertex 1.73120970770261 3.31831671830375 0.54 + vertex 2.73097085950691 3.29646175678623 0.0 + vertex 1.73120970770261 3.31831671830375 0.0 + endloop +endfacet +facet normal 0.0218549615175129 0.999761151804304 0.0 + outer loop + vertex 2.73097085950691 3.29646175678623 0.0 + vertex 1.73120970770261 3.31831671830375 0.54 + vertex 2.73097085950691 3.29646175678623 0.54 + endloop +endfacet +facet normal 0.876746035938074 0.480953624029254 0.0 + outer loop + vertex 2.73097085950691 3.29646175678623 0.0 + vertex 3.21192448353617 2.41971572084816 0.54 + vertex 3.21192448353617 2.41971572084816 0.0 + endloop +endfacet +facet normal 0.876746035938074 0.480953624029254 0.0 + outer loop + vertex 3.21192448353617 2.41971572084816 0.54 + vertex 2.73097085950691 3.29646175678623 0.0 + vertex 2.73097085950691 3.29646175678623 0.54 + endloop +endfacet +facet normal 0.854891074420561 -0.51880752777505 0.0 + outer loop + vertex 3.21192448353617 2.41971572084816 0.0 + vertex 2.69311695576112 1.5648246464276 0.54 + vertex 2.69311695576112 1.5648246464276 0.0 + endloop +endfacet +facet normal 0.854891074420561 -0.51880752777505 0.0 + outer loop + vertex 2.69311695576112 1.5648246464276 0.54 + vertex 3.21192448353617 2.41971572084816 0.0 + vertex 3.21192448353617 2.41971572084816 0.54 + endloop +endfacet +facet normal -0.0218549615175129 -0.999761151804304 -0.0 + outer loop + vertex 2.69311695576112 1.5648246464276 0.54 + vertex 1.69335580395681 1.58667960794511 0.0 + vertex 2.69311695576112 1.5648246464276 0.0 + endloop +endfacet +facet normal -0.0218549615175129 -0.999761151804304 -0.0 + outer loop + vertex 1.69335580395681 1.58667960794511 0.0 + vertex 2.69311695576112 1.5648246464276 0.54 + vertex 1.69335580395681 1.58667960794511 0.54 + endloop +endfacet +facet normal -0.876746035938074 -0.480953624029255 -0.0 + outer loop + vertex 1.21240217992756 2.46342564388318 0.54 + vertex 1.69335580395681 1.58667960794511 0.0 + vertex 1.69335580395681 1.58667960794511 0.54 + endloop +endfacet +facet normal -0.876746035938074 -0.480953624029255 -0.0 + outer loop + vertex 1.69335580395681 1.58667960794511 0.0 + vertex 1.21240217992756 2.46342564388318 0.54 + vertex 1.21240217992756 2.46342564388318 0.0 + endloop +endfacet +facet normal 7.93222929432366e-016 4.89694648390394e-016 -1.0 + outer loop + vertex 2.97826240317018 -0.636443757595701 1.44368526932165e-015 + vertex 1.4493076566905 -1.10792799192095 0.0 + vertex 2.10637516234703 0.776146996759498 1.44382245079469e-015 + endloop +endfacet +facet normal -7.93222929432366e-016 -4.89694648390394e-016 1.0 + outer loop + vertex 2.97826240317018 -0.636443757595701 0.880000000000001 + vertex 2.10637516234703 0.776146996759498 0.880000000000001 + vertex 1.4493076566905 -1.10792799192095 0.88 + endloop +endfacet +facet normal -0.944226355661639 0.329297113977544 -5.87726961439968e-016 + outer loop + vertex 2.10637516234703 0.776146996759498 0.880000000000001 + vertex 1.4493076566905 -1.10792799192095 0.0 + vertex 1.4493076566905 -1.10792799192095 0.88 + endloop +endfacet +facet normal -0.944226355661639 0.329297113977544 -5.87726961439968e-016 + outer loop + vertex 1.4493076566905 -1.10792799192095 0.0 + vertex 2.10637516234703 0.776146996759498 0.880000000000001 + vertex 2.10637516234703 0.776146996759498 1.44382245079469e-015 + endloop +endfacet +facet normal 0.850958285756144 0.525233277604309 9.32203549451609e-016 + outer loop + vertex 2.10637516234703 0.776146996759498 1.44382245079469e-015 + vertex 2.97826240317018 -0.636443757595701 0.880000000000001 + vertex 2.97826240317018 -0.636443757595701 1.44368526932165e-015 + endloop +endfacet +facet normal 0.850958285756144 0.525233277604309 9.32203549451609e-016 + outer loop + vertex 2.97826240317018 -0.636443757595701 0.880000000000001 + vertex 2.10637516234703 0.776146996759498 1.44382245079469e-015 + vertex 2.10637516234703 0.776146996759498 0.880000000000001 + endloop +endfacet +facet normal 0.294677646453279 -0.955596716549799 -2.34205532155964e-016 + outer loop + vertex 2.97826240317018 -0.636443757595701 1.44368526932165e-015 + vertex 1.4493076566905 -1.10792799192095 0.88 + vertex 1.4493076566905 -1.10792799192095 0.0 + endloop +endfacet +facet normal 0.294677646453279 -0.955596716549799 -2.34205532155964e-016 + outer loop + vertex 1.4493076566905 -1.10792799192095 0.88 + vertex 2.97826240317018 -0.636443757595701 1.44368526932165e-015 + vertex 2.97826240317018 -0.636443757595701 0.880000000000001 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.3138052634165 1.0837099501956 0.0 + vertex 3.16916924809083 0.370587344831611 0.0 + vertex 3.0733838074778 0.370587344831611 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.16916924809083 0.370587344831611 0.0 + vertex 3.3138052634165 1.0837099501956 0.0 + vertex 3.23382442050462 0.569821061306707 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.23382442050462 0.569821061306707 0.0 + vertex 3.3138052634165 1.0837099501956 0.0 + vertex 3.26016541667321 0.650759758624714 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.26016541667321 0.650759758624714 0.0 + vertex 3.3138052634165 1.0837099501956 0.0 + vertex 3.36983974617512 0.986966655176438 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.36983974617512 0.986966655176438 0.0 + vertex 3.3138052634165 1.0837099501956 0.0 + vertex 3.43066350096439 1.0837099501956 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.36983974617512 0.986966655176438 0.0 + vertex 3.43066350096439 1.0837099501956 0.0 + vertex 3.4799930028801 0.650759758624714 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.4799930028801 0.650759758624714 0.0 + vertex 3.43066350096439 1.0837099501956 0.0 + vertex 3.67108495690309 0.370587344831611 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.4799930028801 0.650759758624714 0.0 + vertex 3.67108495690309 0.370587344831611 0.0 + vertex 3.50633399904868 0.569821061306707 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.50633399904868 0.569821061306707 0.0 + vertex 3.67108495690309 0.370587344831611 0.0 + vertex 3.57098917146248 0.370587344831611 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.26016541667321 0.650759758624714 0.0 + vertex 3.50633399904868 0.569821061306707 0.0 + vertex 3.23382442050462 0.569821061306707 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.50633399904868 0.569821061306707 0.0 + vertex 3.26016541667321 0.650759758624714 0.0 + vertex 3.4799930028801 0.650759758624714 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.81895373084945 0.907105544065327 0.0 + vertex 3.79273246648164 0.767139068969542 0.0 + vertex 3.78491996648164 0.892887392724331 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.79273246648164 0.767139068969542 0.0 + vertex 3.81895373084945 0.907105544065327 0.0 + vertex 3.80039530173068 0.800185045981036 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.80039530173068 0.800185045981036 0.0 + vertex 3.81895373084945 0.907105544065327 0.0 + vertex 3.8233838074778 0.823652478931228 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.8233838074778 0.823652478931228 0.0 + vertex 3.81895373084945 0.907105544065327 0.0 + vertex 3.857477437746 0.915636434869925 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.8233838074778 0.823652478931228 0.0 + vertex 3.857477437746 0.915636434869925 0.0 + vertex 3.85750737069619 0.837661099620883 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.85750737069619 0.837661099620883 0.0 + vertex 3.857477437746 0.915636434869925 0.0 + vertex 3.90049108717129 0.918480065138124 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.85750737069619 0.837661099620883 0.0 + vertex 3.90049108717129 0.918480065138124 0.0 + vertex 3.89857537835903 0.842330639850768 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.89857537835903 0.842330639850768 0.0 + vertex 3.90049108717129 0.918480065138124 0.0 + vertex 3.94149922893374 0.838379490425481 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.94149922893374 0.838379490425481 0.0 + vertex 3.90049108717129 0.918480065138124 0.0 + vertex 3.99005047414447 0.907464739467626 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.94149922893374 0.838379490425481 0.0 + vertex 3.99005047414447 0.907464739467626 0.0 + vertex 3.98406388410616 0.826526042149619 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.98406388410616 0.826526042149619 0.0 + vertex 3.99005047414447 0.907464739467626 0.0 + vertex 4.02447336686478 0.807847881230078 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.02447336686478 0.807847881230078 0.0 + vertex 3.99005047414447 0.907464739467626 0.0 + vertex 4.06572097222876 0.879686961689848 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.02447336686478 0.807847881230078 0.0 + vertex 4.06572097222876 0.879686961689848 0.0 + vertex 4.06093170019811 0.783422593873756 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.06093170019811 0.783422593873756 0.0 + vertex 4.06572097222876 0.879686961689848 0.0 + vertex 4.06572097222876 0.783422593873756 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.70419079981497 0.789648647513603 0.0 + vertex 3.70772288793757 0.706375180080653 0.0 + vertex 3.70077844349313 0.755165888892914 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.70772288793757 0.706375180080653 0.0 + vertex 3.70419079981497 0.789648647513603 0.0 + vertex 3.71442786878049 0.820778915712837 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.70772288793757 0.706375180080653 0.0 + vertex 3.71442786878049 0.820778915712837 0.0 + vertex 3.72855622127091 0.665367038318201 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.72855622127091 0.665367038318201 0.0 + vertex 3.71442786878049 0.820778915712837 0.0 + vertex 3.73148965038968 0.848556693490615 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.72855622127091 0.665367038318201 0.0 + vertex 3.73148965038968 0.848556693490615 0.0 + vertex 3.76471522510232 0.633099318011688 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.76471522510232 0.633099318011688 0.0 + vertex 3.73148965038968 0.848556693490615 0.0 + vertex 3.75537614464255 0.872981980846936 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.76471522510232 0.633099318011688 0.0 + vertex 3.75537614464255 0.872981980846936 0.0 + vertex 3.78491996648164 0.892887392724331 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.76471522510232 0.633099318011688 0.0 + vertex 3.78491996648164 0.892887392724331 0.0 + vertex 3.79273246648164 0.767139068969542 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.76471522510232 0.633099318011688 0.0 + vertex 3.79273246648164 0.767139068969542 0.0 + vertex 3.79728227491075 0.737864643682186 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.76471522510232 0.633099318011688 0.0 + vertex 3.79728227491075 0.737864643682186 0.0 + vertex 3.81763668104102 0.610529873567243 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.81763668104102 0.610529873567243 0.0 + vertex 3.79728227491075 0.737864643682186 0.0 + vertex 3.81093170019811 0.717091176249235 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.81763668104102 0.610529873567243 0.0 + vertex 3.81093170019811 0.717091176249235 0.0 + vertex 3.83463859674983 0.702064835253067 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.81763668104102 0.610529873567243 0.0 + vertex 3.83463859674983 0.702064835253067 0.0 + vertex 3.86888189176899 0.598077766287549 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.86888189176899 0.598077766287549 0.0 + vertex 3.83463859674983 0.702064835253067 0.0 + vertex 3.86936081897206 0.690031789276055 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.86888189176899 0.598077766287549 0.0 + vertex 3.86936081897206 0.690031789276055 0.0 + vertex 3.91390104885711 0.588020295023182 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.91390104885711 0.588020295023182 0.0 + vertex 3.86936081897206 0.690031789276055 0.0 + vertex 3.91414051245865 0.679734854410155 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.91390104885711 0.588020295023182 0.0 + vertex 3.91414051245865 0.679734854410155 0.0 + vertex 3.95167643199888 0.575687919544254 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.95167643199888 0.575687919544254 0.0 + vertex 3.91414051245865 0.679734854410155 0.0 + vertex 3.96610411399121 0.668001137935059 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.95167643199888 0.575687919544254 0.0 + vertex 3.96610411399121 0.668001137935059 0.0 + vertex 3.97592212165405 0.560721444448469 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.97592212165405 0.560721444448469 0.0 + vertex 3.96610411399121 0.668001137935059 0.0 + vertex 4.01812758142416 0.648245390808622 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.97592212165405 0.560721444448469 0.0 + vertex 4.01812758142416 0.648245390808622 0.0 + vertex 3.98903275383796 0.540486770118967 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.98903275383796 0.540486770118967 0.0 + vertex 4.01812758142416 0.648245390808622 0.0 + vertex 3.99340296456593 0.512349796938891 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.99340296456593 0.512349796938891 0.0 + vertex 4.01812758142416 0.648245390808622 0.0 + vertex 4.00112566571535 0.38543408812663 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.00112566571535 0.38543408812663 0.0 + vertex 4.01812758142416 0.648245390808622 0.0 + vertex 4.03171714081114 0.406506885061496 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.03171714081114 0.406506885061496 0.0 + vertex 4.01812758142416 0.648245390808622 0.0 + vertex 4.05542403736286 0.61867163601935 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.03171714081114 0.406506885061496 0.0 + vertex 4.05542403736286 0.61867163601935 0.0 + vertex 4.05488524425941 0.430752574716668 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.05488524425941 0.430752574716668 0.0 + vertex 4.05542403736286 0.61867163601935 0.0 + vertex 4.07170756226708 0.459428341000193 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.07170756226708 0.459428341000193 0.0 + vertex 4.05542403736286 0.61867163601935 0.0 + vertex 4.07787375000654 0.577962823758814 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.07170756226708 0.459428341000193 0.0 + vertex 4.07787375000654 0.577962823758814 0.0 + vertex 4.08194463123259 0.491217134103642 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.08194463123259 0.491217134103642 0.0 + vertex 4.07787375000654 0.577962823758814 0.0 + vertex 4.08535698755443 0.524801904218584 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.70317307950846 0.503729107283718 0.0 + vertex 3.6983838074778 0.402675467436975 0.0 + vertex 3.6983838074778 0.503729107283718 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.6983838074778 0.402675467436975 0.0 + vertex 3.70317307950846 0.503729107283718 0.0 + vertex 3.77573055077282 0.372024126440806 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.77573055077282 0.372024126440806 0.0 + vertex 3.70317307950846 0.503729107283718 0.0 + vertex 3.73023246648164 0.483614164754982 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.77573055077282 0.372024126440806 0.0 + vertex 3.73023246648164 0.483614164754982 0.0 + vertex 3.77165966954677 0.460625659007856 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.77573055077282 0.372024126440806 0.0 + vertex 3.77165966954677 0.460625659007856 0.0 + vertex 3.82218648947014 0.441947498088316 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.77573055077282 0.372024126440806 0.0 + vertex 3.82218648947014 0.441947498088316 0.0 + vertex 3.82416206418278 0.361248264371841 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.82416206418278 0.361248264371841 0.0 + vertex 3.82218648947014 0.441947498088316 0.0 + vertex 3.87941829023642 0.433805735636209 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.82416206418278 0.361248264371841 0.0 + vertex 3.87941829023642 0.433805735636209 0.0 + vertex 3.87606579981497 0.357656310348852 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.87606579981497 0.357656310348852 0.0 + vertex 3.87941829023642 0.433805735636209 0.0 + vertex 3.92605382663489 0.360769337168776 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.92605382663489 0.360769337168776 0.0 + vertex 3.87941829023642 0.433805735636209 0.0 + vertex 3.92539530173068 0.438834471268392 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.92605382663489 0.360769337168776 0.0 + vertex 3.92539530173068 0.438834471268392 0.0 + vertex 3.96179376916363 0.451526042149618 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.92605382663489 0.360769337168776 0.0 + vertex 3.96179376916363 0.451526042149618 0.0 + vertex 3.96682250479581 0.370108417628546 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.96682250479581 0.370108417628546 0.0 + vertex 3.96179376916363 0.451526042149618 0.0 + vertex 3.98597959291842 0.47499347509981 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 3.96682250479581 0.370108417628546 0.0 + vertex 3.98597959291842 0.47499347509981 0.0 + vertex 4.00112566571535 0.38543408812663 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.00112566571535 0.38543408812663 0.0 + vertex 3.98597959291842 0.47499347509981 0.0 + vertex 3.99340296456593 0.512349796938891 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.13702125958508 0.702962823758814 0.0 + vertex 4.13684166188393 0.571078245214753 0.0 + vertex 4.13229185345481 0.63734979693889 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.13684166188393 0.571078245214753 0.0 + vertex 4.13702125958508 0.702962823758814 0.0 + vertex 4.15049108717129 0.514026042149619 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.15049108717129 0.514026042149619 0.0 + vertex 4.13702125958508 0.702962823758814 0.0 + vertex 4.15120947797589 0.758997306517435 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.15049108717129 0.514026042149619 0.0 + vertex 4.15120947797589 0.758997306517435 0.0 + vertex 4.17240200671152 0.466073455942722 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.17240200671152 0.466073455942722 0.0 + vertex 4.15120947797589 0.758997306517435 0.0 + vertex 4.17329999521727 0.80617163601935 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.17240200671152 0.466073455942722 0.0 + vertex 4.17329999521727 0.80617163601935 0.0 + vertex 4.20173629789926 0.427100754793297 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.20173629789926 0.427100754793297 0.0 + vertex 4.17329999521727 0.80617163601935 0.0 + vertex 4.20173629789926 0.845204203069159 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.20173629789926 0.427100754793297 0.0 + vertex 4.20173629789926 0.845204203069159 0.0 + vertex 4.23723677682646 0.876154873567243 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.20173629789926 0.427100754793297 0.0 + vertex 4.23723677682646 0.876154873567243 0.0 + vertex 4.22568265805252 0.63734979693889 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.22568265805252 0.63734979693889 0.0 + vertex 4.23723677682646 0.876154873567243 0.0 + vertex 4.22833172414447 0.68298257950594 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.22833172414447 0.68298257950594 0.0 + vertex 4.23723677682646 0.876154873567243 0.0 + vertex 4.23627892242033 0.723137632187933 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.23627892242033 0.723137632187933 0.0 + vertex 4.23723677682646 0.876154873567243 0.0 + vertex 4.2495242528801 0.757814954984868 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.2495242528801 0.757814954984868 0.0 + vertex 4.23723677682646 0.876154873567243 0.0 + vertex 4.28051982280347 0.899083513413986 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.2495242528801 0.757814954984868 0.0 + vertex 4.28051982280347 0.899083513413986 0.0 + vertex 4.26806771552378 0.787014547896745 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.26806771552378 0.787014547896745 0.0 + vertex 4.28051982280347 0.899083513413986 0.0 + vertex 4.29098138889543 0.810167684869925 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.29098138889543 0.810167684869925 0.0 + vertex 4.28051982280347 0.899083513413986 0.0 + vertex 4.3294302634165 0.913271731804791 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.29098138889543 0.810167684869925 0.0 + vertex 4.3294302634165 0.913271731804791 0.0 + vertex 4.31733735153911 0.826705639850768 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.31733735153911 0.826705639850768 0.0 + vertex 4.3294302634165 0.913271731804791 0.0 + vertex 4.34713560345482 0.836628412839274 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.34713560345482 0.836628412839274 0.0 + vertex 4.3294302634165 0.913271731804791 0.0 + vertex 4.38181292625175 0.918001137935059 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.34713560345482 0.836628412839274 0.0 + vertex 4.38181292625175 0.918001137935059 0.0 + vertex 4.38037614464255 0.839936003835442 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.38037614464255 0.839936003835442 0.0 + vertex 4.38181292625175 0.918001137935059 0.0 + vertex 4.4292267193552 0.832752095789465 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.4292267193552 0.832752095789465 0.0 + vertex 4.38181292625175 0.918001137935059 0.0 + vertex 4.42509597222876 0.914888111115136 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.4292267193552 0.832752095789465 0.0 + vertex 4.42509597222876 0.914888111115136 0.0 + vertex 4.46682250479581 0.905549030655366 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.4292267193552 0.832752095789465 0.0 + vertex 4.46682250479581 0.905549030655366 0.0 + vertex 4.47328802203719 0.815989643682186 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.47328802203719 0.815989643682186 0.0 + vertex 4.46682250479581 0.905549030655366 0.0 + vertex 4.53985890326324 0.876813398471458 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.47328802203719 0.815989643682186 0.0 + vertex 4.53985890326324 0.876813398471458 0.0 + vertex 4.50992595307167 0.794437919544255 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.50992595307167 0.794437919544255 0.0 + vertex 4.53985890326324 0.876813398471458 0.0 + vertex 4.53459070402953 0.775759758624714 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.53459070402953 0.775759758624714 0.0 + vertex 4.53985890326324 0.876813398471458 0.0 + vertex 4.53985890326324 0.775759758624714 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.22568265805252 0.63734979693889 0.0 + vertex 4.23801503353144 0.396988206900576 0.0 + vertex 4.20173629789926 0.427100754793297 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.23801503353144 0.396988206900576 0.0 + vertex 4.22568265805252 0.63734979693889 0.0 + vertex 4.2282419252939 0.592555136977205 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.23801503353144 0.396988206900576 0.0 + vertex 4.2282419252939 0.592555136977205 0.0 + vertex 4.23591972701803 0.552998743299044 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.23801503353144 0.396988206900576 0.0 + vertex 4.23591972701803 0.552998743299044 0.0 + vertex 4.24871606322493 0.518680615904408 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.23801503353144 0.396988206900576 0.0 + vertex 4.24871606322493 0.518680615904408 0.0 + vertex 4.28075928640501 0.375616080463795 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.28075928640501 0.375616080463795 0.0 + vertex 4.24871606322493 0.518680615904408 0.0 + vertex 4.26663093391458 0.489600754793297 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.28075928640501 0.375616080463795 0.0 + vertex 4.26663093391458 0.489600754793297 0.0 + vertex 4.28909561303336 0.466447617820117 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.28075928640501 0.375616080463795 0.0 + vertex 4.28909561303336 0.466447617820117 0.0 + vertex 4.32901120211382 0.362864643682186 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.32901120211382 0.362864643682186 0.0 + vertex 4.28909561303336 0.466447617820117 0.0 + vertex 4.31554137452761 0.449909662839274 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.32901120211382 0.362864643682186 0.0 + vertex 4.31554137452761 0.449909662839274 0.0 + vertex 4.34596821839734 0.439986889850768 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.32901120211382 0.362864643682186 0.0 + vertex 4.34596821839734 0.439986889850768 0.0 + vertex 4.38181292625175 0.358614164754983 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.38181292625175 0.358614164754983 0.0 + vertex 4.34596821839734 0.439986889850768 0.0 + vertex 4.38037614464255 0.436679298854599 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.38181292625175 0.358614164754983 0.0 + vertex 4.38037614464255 0.436679298854599 0.0 + vertex 4.42335986111765 0.441229107283718 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.38181292625175 0.358614164754983 0.0 + vertex 4.42335986111765 0.441229107283718 0.0 + vertex 4.42874779215213 0.362206118777971 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.42874779215213 0.362206118777971 0.0 + vertex 4.42335986111765 0.441229107283718 0.0 + vertex 4.46418840517895 0.454878532571074 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.42874779215213 0.362206118777971 0.0 + vertex 4.46418840517895 0.454878532571074 0.0 + vertex 4.46993553161574 0.372503053643871 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.46993553161574 0.372503053643871 0.0 + vertex 4.46418840517895 0.454878532571074 0.0 + vertex 4.5016644588188 0.475711865904408 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.46993553161574 0.372503053643871 0.0 + vertex 4.5016644588188 0.475711865904408 0.0 + vertex 4.50633399904869 0.385673551728163 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.50633399904869 0.385673551728163 0.0 + vertex 4.5016644588188 0.475711865904408 0.0 + vertex 4.53459070402953 0.501813398471458 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.50633399904869 0.385673551728163 0.0 + vertex 4.53459070402953 0.501813398471458 0.0 + vertex 4.53985890326324 0.400759758624714 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.53985890326324 0.400759758624714 0.0 + vertex 4.53459070402953 0.501813398471458 0.0 + vertex 4.53985890326324 0.501813398471458 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.71370947797589 0.905549030655366 0.0 + vertex 4.62367116379964 0.370587344831611 0.0 + vertex 4.62367116379964 0.905549030655366 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.62367116379964 0.370587344831611 0.0 + vertex 4.71370947797589 0.905549030655366 0.0 + vertex 4.71370947797589 0.370587344831611 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.71993553161574 1.08849922222625 0.0 + vertex 4.6174451101598 0.995108417628546 0.0 + vertex 4.6174451101598 1.08849922222625 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.6174451101598 0.995108417628546 0.0 + vertex 4.71993553161574 1.08849922222625 0.0 + vertex 4.71993553161574 0.995108417628546 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.93784740901037 0.905549030655366 0.0 + vertex 4.84780909483413 0.370587344831611 0.0 + vertex 4.84780909483413 0.905549030655366 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.84780909483413 0.370587344831611 0.0 + vertex 4.93784740901037 0.905549030655366 0.0 + vertex 4.93784740901037 0.370587344831611 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.94407346265022 1.08849922222625 0.0 + vertex 4.84158304119428 0.995108417628546 0.0 + vertex 4.84158304119428 1.08849922222625 0.0 + endloop +endfacet +facet normal -0.0 -0.0 -1.0 + outer loop + vertex 4.84158304119428 0.995108417628546 0.0 + vertex 4.94407346265022 1.08849922222625 0.0 + vertex 4.94407346265022 0.995108417628546 0.0 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.50633399904868 0.569821061306707 0.2 + vertex 3.26016541667321 0.650759758624714 0.2 + vertex 3.23382442050462 0.569821061306707 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.26016541667321 0.650759758624714 0.2 + vertex 3.50633399904868 0.569821061306707 0.2 + vertex 3.4799930028801 0.650759758624714 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.16916924809083 0.370587344831611 0.2 + vertex 3.3138052634165 1.0837099501956 0.2 + vertex 3.0733838074778 0.370587344831611 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.3138052634165 1.0837099501956 0.2 + vertex 3.16916924809083 0.370587344831611 0.2 + vertex 3.23382442050462 0.569821061306707 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.3138052634165 1.0837099501956 0.2 + vertex 3.23382442050462 0.569821061306707 0.2 + vertex 3.26016541667321 0.650759758624714 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.3138052634165 1.0837099501956 0.2 + vertex 3.26016541667321 0.650759758624714 0.2 + vertex 3.36983974617512 0.986966655176438 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.3138052634165 1.0837099501956 0.2 + vertex 3.36983974617512 0.986966655176438 0.2 + vertex 3.43066350096439 1.0837099501956 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.43066350096439 1.0837099501956 0.2 + vertex 3.36983974617512 0.986966655176438 0.2 + vertex 3.4799930028801 0.650759758624714 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.43066350096439 1.0837099501956 0.2 + vertex 3.4799930028801 0.650759758624714 0.2 + vertex 3.67108495690309 0.370587344831611 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.67108495690309 0.370587344831611 0.2 + vertex 3.4799930028801 0.650759758624714 0.2 + vertex 3.50633399904868 0.569821061306707 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.67108495690309 0.370587344831611 0.2 + vertex 3.50633399904868 0.569821061306707 0.2 + vertex 3.57098917146248 0.370587344831611 0.2 + endloop +endfacet +facet normal -0.947595870336326 0.319471542584846 0.0 + outer loop + vertex 3.3138052634165 1.0837099501956 0.2 + vertex 3.0733838074778 0.370587344831611 0.0 + vertex 3.0733838074778 0.370587344831611 0.2 + endloop +endfacet +facet normal -0.947595870336326 0.319471542584846 0.0 + outer loop + vertex 3.0733838074778 0.370587344831611 0.0 + vertex 3.3138052634165 1.0837099501956 0.2 + vertex 3.3138052634165 1.0837099501956 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 3.3138052634165 1.0837099501956 0.2 + vertex 3.43066350096439 1.0837099501956 0.0 + vertex 3.3138052634165 1.0837099501956 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 3.43066350096439 1.0837099501956 0.0 + vertex 3.3138052634165 1.0837099501956 0.2 + vertex 3.43066350096439 1.0837099501956 0.2 + endloop +endfacet +facet normal 0.947595870336326 0.319471542584846 0.0 + outer loop + vertex 3.43066350096439 1.0837099501956 0.0 + vertex 3.67108495690309 0.370587344831611 0.2 + vertex 3.67108495690309 0.370587344831611 0.0 + endloop +endfacet +facet normal 0.947595870336326 0.319471542584846 0.0 + outer loop + vertex 3.67108495690309 0.370587344831611 0.2 + vertex 3.43066350096439 1.0837099501956 0.0 + vertex 3.43066350096439 1.0837099501956 0.2 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 3.67108495690309 0.370587344831611 0.2 + vertex 3.57098917146248 0.370587344831611 0.0 + vertex 3.67108495690309 0.370587344831611 0.0 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 3.57098917146248 0.370587344831611 0.0 + vertex 3.67108495690309 0.370587344831611 0.2 + vertex 3.57098917146248 0.370587344831611 0.2 + endloop +endfacet +facet normal -0.951168400556076 -0.308672437680457 0.0 + outer loop + vertex 3.50633399904868 0.569821061306707 0.2 + vertex 3.57098917146248 0.370587344831611 0.0 + vertex 3.57098917146248 0.370587344831611 0.2 + endloop +endfacet +facet normal -0.951168400556076 -0.308672437680457 0.0 + outer loop + vertex 3.57098917146248 0.370587344831611 0.0 + vertex 3.50633399904868 0.569821061306707 0.2 + vertex 3.50633399904868 0.569821061306707 0.0 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 3.50633399904868 0.569821061306707 0.2 + vertex 3.23382442050462 0.569821061306707 0.0 + vertex 3.50633399904868 0.569821061306707 0.0 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 3.23382442050462 0.569821061306707 0.0 + vertex 3.50633399904868 0.569821061306707 0.2 + vertex 3.23382442050462 0.569821061306707 0.2 + endloop +endfacet +facet normal 0.951168400556076 -0.308672437680457 0.0 + outer loop + vertex 3.23382442050462 0.569821061306707 0.0 + vertex 3.16916924809083 0.370587344831611 0.2 + vertex 3.16916924809083 0.370587344831611 0.0 + endloop +endfacet +facet normal 0.951168400556076 -0.308672437680457 0.0 + outer loop + vertex 3.16916924809083 0.370587344831611 0.2 + vertex 3.23382442050462 0.569821061306707 0.0 + vertex 3.23382442050462 0.569821061306707 0.2 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 3.16916924809083 0.370587344831611 0.2 + vertex 3.0733838074778 0.370587344831611 0.0 + vertex 3.16916924809083 0.370587344831611 0.0 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 3.0733838074778 0.370587344831611 0.0 + vertex 3.16916924809083 0.370587344831611 0.2 + vertex 3.0733838074778 0.370587344831611 0.2 + endloop +endfacet +facet normal -0.950295217341758 -0.311350284884052 0.0 + outer loop + vertex 3.36983974617512 0.986966655176438 0.2 + vertex 3.4799930028801 0.650759758624714 0.0 + vertex 3.4799930028801 0.650759758624714 0.2 + endloop +endfacet +facet normal -0.950295217341758 -0.311350284884052 0.0 + outer loop + vertex 3.4799930028801 0.650759758624714 0.0 + vertex 3.36983974617512 0.986966655176438 0.2 + vertex 3.36983974617512 0.986966655176438 0.0 + endloop +endfacet +facet normal 0.950695123572524 -0.310127041735196 0.0 + outer loop + vertex 3.36983974617512 0.986966655176438 0.0 + vertex 3.26016541667321 0.650759758624714 0.2 + vertex 3.26016541667321 0.650759758624714 0.0 + endloop +endfacet +facet normal 0.950695123572524 -0.310127041735196 0.0 + outer loop + vertex 3.26016541667321 0.650759758624714 0.2 + vertex 3.36983974617512 0.986966655176438 0.0 + vertex 3.36983974617512 0.986966655176438 0.2 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 3.26016541667321 0.650759758624714 0.2 + vertex 3.4799930028801 0.650759758624714 0.0 + vertex 3.26016541667321 0.650759758624714 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 3.4799930028801 0.650759758624714 0.0 + vertex 3.26016541667321 0.650759758624714 0.2 + vertex 3.4799930028801 0.650759758624714 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.77573055077282 0.372024126440806 0.2 + vertex 3.6983838074778 0.503729107283718 0.2 + vertex 3.6983838074778 0.402675467436975 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.6983838074778 0.503729107283718 0.2 + vertex 3.77573055077282 0.372024126440806 0.2 + vertex 3.70317307950846 0.503729107283718 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.70317307950846 0.503729107283718 0.2 + vertex 3.77573055077282 0.372024126440806 0.2 + vertex 3.73023246648164 0.483614164754982 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.73023246648164 0.483614164754982 0.2 + vertex 3.77573055077282 0.372024126440806 0.2 + vertex 3.77165966954677 0.460625659007856 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.77165966954677 0.460625659007856 0.2 + vertex 3.77573055077282 0.372024126440806 0.2 + vertex 3.82218648947014 0.441947498088316 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.82218648947014 0.441947498088316 0.2 + vertex 3.77573055077282 0.372024126440806 0.2 + vertex 3.82416206418278 0.361248264371841 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.82218648947014 0.441947498088316 0.2 + vertex 3.82416206418278 0.361248264371841 0.2 + vertex 3.87941829023642 0.433805735636209 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.87941829023642 0.433805735636209 0.2 + vertex 3.82416206418278 0.361248264371841 0.2 + vertex 3.87606579981497 0.357656310348852 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.87941829023642 0.433805735636209 0.2 + vertex 3.87606579981497 0.357656310348852 0.2 + vertex 3.92605382663489 0.360769337168776 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.87941829023642 0.433805735636209 0.2 + vertex 3.92605382663489 0.360769337168776 0.2 + vertex 3.92539530173068 0.438834471268392 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.92539530173068 0.438834471268392 0.2 + vertex 3.92605382663489 0.360769337168776 0.2 + vertex 3.96179376916363 0.451526042149618 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.96179376916363 0.451526042149618 0.2 + vertex 3.92605382663489 0.360769337168776 0.2 + vertex 3.96682250479581 0.370108417628546 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.96179376916363 0.451526042149618 0.2 + vertex 3.96682250479581 0.370108417628546 0.2 + vertex 3.98597959291842 0.47499347509981 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.98597959291842 0.47499347509981 0.2 + vertex 3.96682250479581 0.370108417628546 0.2 + vertex 4.00112566571535 0.38543408812663 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.98597959291842 0.47499347509981 0.2 + vertex 4.00112566571535 0.38543408812663 0.2 + vertex 3.99340296456593 0.512349796938891 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.70772288793757 0.706375180080653 0.2 + vertex 3.70419079981497 0.789648647513603 0.2 + vertex 3.70077844349313 0.755165888892914 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.70419079981497 0.789648647513603 0.2 + vertex 3.70772288793757 0.706375180080653 0.2 + vertex 3.71442786878049 0.820778915712837 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.71442786878049 0.820778915712837 0.2 + vertex 3.70772288793757 0.706375180080653 0.2 + vertex 3.72855622127091 0.665367038318201 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.71442786878049 0.820778915712837 0.2 + vertex 3.72855622127091 0.665367038318201 0.2 + vertex 3.73148965038968 0.848556693490615 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.73148965038968 0.848556693490615 0.2 + vertex 3.72855622127091 0.665367038318201 0.2 + vertex 3.76471522510232 0.633099318011688 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.73148965038968 0.848556693490615 0.2 + vertex 3.76471522510232 0.633099318011688 0.2 + vertex 3.75537614464255 0.872981980846936 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.75537614464255 0.872981980846936 0.2 + vertex 3.76471522510232 0.633099318011688 0.2 + vertex 3.78491996648164 0.892887392724331 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.78491996648164 0.892887392724331 0.2 + vertex 3.76471522510232 0.633099318011688 0.2 + vertex 3.79273246648164 0.767139068969542 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.79273246648164 0.767139068969542 0.2 + vertex 3.76471522510232 0.633099318011688 0.2 + vertex 3.79728227491075 0.737864643682186 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.79728227491075 0.737864643682186 0.2 + vertex 3.76471522510232 0.633099318011688 0.2 + vertex 3.81763668104102 0.610529873567243 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.79728227491075 0.737864643682186 0.2 + vertex 3.81763668104102 0.610529873567243 0.2 + vertex 3.81093170019811 0.717091176249235 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.81093170019811 0.717091176249235 0.2 + vertex 3.81763668104102 0.610529873567243 0.2 + vertex 3.83463859674983 0.702064835253067 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.83463859674983 0.702064835253067 0.2 + vertex 3.81763668104102 0.610529873567243 0.2 + vertex 3.86888189176899 0.598077766287549 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.83463859674983 0.702064835253067 0.2 + vertex 3.86888189176899 0.598077766287549 0.2 + vertex 3.86936081897206 0.690031789276055 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.86936081897206 0.690031789276055 0.2 + vertex 3.86888189176899 0.598077766287549 0.2 + vertex 3.91390104885711 0.588020295023182 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.86936081897206 0.690031789276055 0.2 + vertex 3.91390104885711 0.588020295023182 0.2 + vertex 3.91414051245865 0.679734854410155 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.91414051245865 0.679734854410155 0.2 + vertex 3.91390104885711 0.588020295023182 0.2 + vertex 3.95167643199888 0.575687919544254 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.91414051245865 0.679734854410155 0.2 + vertex 3.95167643199888 0.575687919544254 0.2 + vertex 3.96610411399121 0.668001137935059 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.96610411399121 0.668001137935059 0.2 + vertex 3.95167643199888 0.575687919544254 0.2 + vertex 3.97592212165405 0.560721444448469 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.96610411399121 0.668001137935059 0.2 + vertex 3.97592212165405 0.560721444448469 0.2 + vertex 4.01812758142416 0.648245390808622 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.01812758142416 0.648245390808622 0.2 + vertex 3.97592212165405 0.560721444448469 0.2 + vertex 3.98903275383796 0.540486770118967 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.01812758142416 0.648245390808622 0.2 + vertex 3.98903275383796 0.540486770118967 0.2 + vertex 3.99340296456593 0.512349796938891 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.01812758142416 0.648245390808622 0.2 + vertex 3.99340296456593 0.512349796938891 0.2 + vertex 4.00112566571535 0.38543408812663 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.01812758142416 0.648245390808622 0.2 + vertex 4.00112566571535 0.38543408812663 0.2 + vertex 4.03171714081114 0.406506885061496 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.01812758142416 0.648245390808622 0.2 + vertex 4.03171714081114 0.406506885061496 0.2 + vertex 4.05542403736286 0.61867163601935 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.05542403736286 0.61867163601935 0.2 + vertex 4.03171714081114 0.406506885061496 0.2 + vertex 4.05488524425941 0.430752574716668 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.05542403736286 0.61867163601935 0.2 + vertex 4.05488524425941 0.430752574716668 0.2 + vertex 4.07170756226708 0.459428341000193 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.05542403736286 0.61867163601935 0.2 + vertex 4.07170756226708 0.459428341000193 0.2 + vertex 4.07787375000654 0.577962823758814 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.07787375000654 0.577962823758814 0.2 + vertex 4.07170756226708 0.459428341000193 0.2 + vertex 4.08194463123259 0.491217134103642 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.07787375000654 0.577962823758814 0.2 + vertex 4.08194463123259 0.491217134103642 0.2 + vertex 4.08535698755443 0.524801904218584 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.79273246648164 0.767139068969542 0.2 + vertex 3.81895373084945 0.907105544065327 0.2 + vertex 3.78491996648164 0.892887392724331 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.81895373084945 0.907105544065327 0.2 + vertex 3.79273246648164 0.767139068969542 0.2 + vertex 3.80039530173068 0.800185045981036 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.81895373084945 0.907105544065327 0.2 + vertex 3.80039530173068 0.800185045981036 0.2 + vertex 3.8233838074778 0.823652478931228 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.81895373084945 0.907105544065327 0.2 + vertex 3.8233838074778 0.823652478931228 0.2 + vertex 3.857477437746 0.915636434869925 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.857477437746 0.915636434869925 0.2 + vertex 3.8233838074778 0.823652478931228 0.2 + vertex 3.85750737069619 0.837661099620883 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.857477437746 0.915636434869925 0.2 + vertex 3.85750737069619 0.837661099620883 0.2 + vertex 3.90049108717129 0.918480065138124 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.90049108717129 0.918480065138124 0.2 + vertex 3.85750737069619 0.837661099620883 0.2 + vertex 3.89857537835903 0.842330639850768 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.90049108717129 0.918480065138124 0.2 + vertex 3.89857537835903 0.842330639850768 0.2 + vertex 3.94149922893374 0.838379490425481 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.90049108717129 0.918480065138124 0.2 + vertex 3.94149922893374 0.838379490425481 0.2 + vertex 3.99005047414447 0.907464739467626 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.99005047414447 0.907464739467626 0.2 + vertex 3.94149922893374 0.838379490425481 0.2 + vertex 3.98406388410616 0.826526042149619 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.99005047414447 0.907464739467626 0.2 + vertex 3.98406388410616 0.826526042149619 0.2 + vertex 4.02447336686478 0.807847881230078 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 3.99005047414447 0.907464739467626 0.2 + vertex 4.02447336686478 0.807847881230078 0.2 + vertex 4.06572097222876 0.879686961689848 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.06572097222876 0.879686961689848 0.2 + vertex 4.02447336686478 0.807847881230078 0.2 + vertex 4.06093170019811 0.783422593873756 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.06572097222876 0.879686961689848 0.2 + vertex 4.06093170019811 0.783422593873756 0.2 + vertex 4.06572097222876 0.783422593873756 0.2 + endloop +endfacet +facet normal -0.0034494236932481 -0.999994050720395 0.0 + outer loop + vertex 3.87606579981497 0.357656310348852 0.2 + vertex 3.82416206418278 0.361248264371841 0.0 + vertex 3.87606579981497 0.357656310348852 0.0 + endloop +endfacet +facet normal -0.143515065226195 -0.989648132445629 0.0 + outer loop + vertex 3.82416206418278 0.361248264371841 0.0 + vertex 3.87606579981497 0.357656310348852 0.2 + vertex 3.82416206418278 0.361248264371841 0.2 + endloop +endfacet +facet normal -0.143515065226195 -0.989648132445629 0.0 + outer loop + vertex 3.82416206418278 0.361248264371841 0.2 + vertex 3.77573055077282 0.372024126440806 0.0 + vertex 3.82416206418278 0.361248264371841 0.0 + endloop +endfacet +facet normal -0.29371898743818 -0.955891812088738 0.0 + outer loop + vertex 3.77573055077282 0.372024126440806 0.0 + vertex 3.82416206418278 0.361248264371841 0.2 + vertex 3.77573055077282 0.372024126440806 0.2 + endloop +endfacet +facet normal -0.29371898743818 -0.955891812088738 0.0 + outer loop + vertex 3.77573055077282 0.372024126440806 0.2 + vertex 3.6983838074778 0.402675467436975 0.0 + vertex 3.77573055077282 0.372024126440806 0.0 + endloop +endfacet +facet normal -0.36841130465666 -0.929662901594539 0.0 + outer loop + vertex 3.6983838074778 0.402675467436975 0.0 + vertex 3.77573055077282 0.372024126440806 0.2 + vertex 3.6983838074778 0.402675467436975 0.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 3.6983838074778 0.503729107283718 0.2 + vertex 3.6983838074778 0.402675467436975 0.0 + vertex 3.6983838074778 0.402675467436975 0.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 3.6983838074778 0.402675467436975 0.0 + vertex 3.6983838074778 0.503729107283718 0.2 + vertex 3.6983838074778 0.503729107283718 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 3.6983838074778 0.503729107283718 0.2 + vertex 3.70317307950846 0.503729107283718 0.0 + vertex 3.6983838074778 0.503729107283718 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 3.70317307950846 0.503729107283718 0.0 + vertex 3.6983838074778 0.503729107283718 0.2 + vertex 3.70317307950846 0.503729107283718 0.2 + endloop +endfacet +facet normal 0.596585492351885 0.802549531378132 0.0 + outer loop + vertex 3.70317307950846 0.503729107283718 0.2 + vertex 3.73023246648164 0.483614164754982 0.0 + vertex 3.70317307950846 0.503729107283718 0.0 + endloop +endfacet +facet normal 0.542091186803624 0.840319668453523 0.0 + outer loop + vertex 3.73023246648164 0.483614164754982 0.0 + vertex 3.70317307950846 0.503729107283718 0.2 + vertex 3.73023246648164 0.483614164754982 0.2 + endloop +endfacet +facet normal 0.542091186803624 0.840319668453523 0.0 + outer loop + vertex 3.73023246648164 0.483614164754982 0.2 + vertex 3.77165966954677 0.460625659007856 0.0 + vertex 3.73023246648164 0.483614164754982 0.0 + endloop +endfacet +facet normal 0.417186946688829 0.908820692718015 0.0 + outer loop + vertex 3.77165966954677 0.460625659007856 0.0 + vertex 3.73023246648164 0.483614164754982 0.2 + vertex 3.77165966954677 0.460625659007856 0.2 + endloop +endfacet +facet normal 0.417186946688829 0.908820692718015 0.0 + outer loop + vertex 3.77165966954677 0.460625659007856 0.2 + vertex 3.82218648947014 0.441947498088316 0.0 + vertex 3.77165966954677 0.460625659007856 0.0 + endloop +endfacet +facet normal 0.245174475646029 0.969478971660394 0.0 + outer loop + vertex 3.82218648947014 0.441947498088316 0.0 + vertex 3.77165966954677 0.460625659007856 0.2 + vertex 3.82218648947014 0.441947498088316 0.2 + endloop +endfacet +facet normal 0.245174475646029 0.969478971660394 0.0 + outer loop + vertex 3.82218648947014 0.441947498088316 0.2 + vertex 3.87941829023642 0.433805735636209 0.0 + vertex 3.82218648947014 0.441947498088316 0.0 + endloop +endfacet +facet normal 0.0161839307351789 0.999869031616621 0.0 + outer loop + vertex 3.87941829023642 0.433805735636209 0.0 + vertex 3.82218648947014 0.441947498088316 0.2 + vertex 3.87941829023642 0.433805735636209 0.2 + endloop +endfacet +facet normal 0.0161839307351789 0.999869031616621 0.0 + outer loop + vertex 3.87941829023642 0.433805735636209 0.2 + vertex 3.92539530173068 0.438834471268392 0.0 + vertex 3.87941829023642 0.433805735636209 0.0 + endloop +endfacet +facet normal -0.220397575605247 0.975410123315998 0.0 + outer loop + vertex 3.92539530173068 0.438834471268392 0.0 + vertex 3.87941829023642 0.433805735636209 0.2 + vertex 3.92539530173068 0.438834471268392 0.2 + endloop +endfacet +facet normal -0.220397575605247 0.975410123315998 0.0 + outer loop + vertex 3.92539530173068 0.438834471268392 0.2 + vertex 3.96179376916363 0.451526042149618 0.0 + vertex 3.92539530173068 0.438834471268392 0.0 + endloop +endfacet +facet normal -0.525168442208563 0.85099830041442 0.0 + outer loop + vertex 3.96179376916363 0.451526042149618 0.0 + vertex 3.92539530173068 0.438834471268392 0.2 + vertex 3.96179376916363 0.451526042149618 0.2 + endloop +endfacet +facet normal -0.525168442208563 0.85099830041442 0.0 + outer loop + vertex 3.96179376916363 0.451526042149618 0.2 + vertex 3.98597959291842 0.47499347509981 0.0 + vertex 3.96179376916363 0.451526042149618 0.0 + endloop +endfacet +facet normal -0.696367753757026 0.71768513397408 0.0 + outer loop + vertex 3.98597959291842 0.47499347509981 0.0 + vertex 3.96179376916363 0.451526042149618 0.2 + vertex 3.98597959291842 0.47499347509981 0.2 + endloop +endfacet +facet normal -0.999778724496402 0.021035732560287 0.0 + outer loop + vertex 3.99340296456593 0.512349796938891 0.2 + vertex 3.98597959291842 0.47499347509981 0.0 + vertex 3.98597959291842 0.47499347509981 0.2 + endloop +endfacet +facet normal -0.980821750211741 0.194906886260025 0.0 + outer loop + vertex 3.98597959291842 0.47499347509981 0.0 + vertex 3.99340296456593 0.512349796938891 0.2 + vertex 3.99340296456593 0.512349796938891 0.0 + endloop +endfacet +facet normal -0.934301067909552 -0.356484942884087 0.0 + outer loop + vertex 3.98903275383796 0.540486770118967 0.2 + vertex 3.99340296456593 0.512349796938891 0.0 + vertex 3.99340296456593 0.512349796938891 0.2 + endloop +endfacet +facet normal -0.999778724496402 0.021035732560287 0.0 + outer loop + vertex 3.99340296456593 0.512349796938891 0.0 + vertex 3.98903275383796 0.540486770118967 0.2 + vertex 3.98903275383796 0.540486770118967 0.0 + endloop +endfacet +facet normal -0.699326547675998 -0.714802336115076 0.0 + outer loop + vertex 3.97592212165405 0.560721444448469 0.2 + vertex 3.98903275383796 0.540486770118967 0.0 + vertex 3.98903275383796 0.540486770118967 0.2 + endloop +endfacet +facet normal -0.934301067909552 -0.356484942884087 0.0 + outer loop + vertex 3.98903275383796 0.540486770118967 0.0 + vertex 3.97592212165405 0.560721444448469 0.2 + vertex 3.97592212165405 0.560721444448469 0.0 + endloop +endfacet +facet normal -0.699326547675998 -0.714802336115076 0.0 + outer loop + vertex 3.97592212165405 0.560721444448469 0.2 + vertex 3.95167643199888 0.575687919544254 0.0 + vertex 3.97592212165405 0.560721444448469 0.0 + endloop +endfacet +facet normal -0.420770342017246 -0.907167194776514 0.0 + outer loop + vertex 3.95167643199888 0.575687919544254 0.0 + vertex 3.97592212165405 0.560721444448469 0.2 + vertex 3.95167643199888 0.575687919544254 0.2 + endloop +endfacet +facet normal -0.420770342017246 -0.907167194776514 0.0 + outer loop + vertex 3.95167643199888 0.575687919544254 0.2 + vertex 3.91390104885711 0.588020295023182 0.0 + vertex 3.95167643199888 0.575687919544254 0.0 + endloop +endfacet +facet normal -0.264491049691579 -0.964388140031309 0.0 + outer loop + vertex 3.91390104885711 0.588020295023182 0.0 + vertex 3.95167643199888 0.575687919544254 0.2 + vertex 3.91390104885711 0.588020295023182 0.2 + endloop +endfacet +facet normal -0.264491049691579 -0.964388140031309 0.0 + outer loop + vertex 3.91390104885711 0.588020295023182 0.2 + vertex 3.86888189176899 0.598077766287549 0.0 + vertex 3.91390104885711 0.588020295023182 0.0 + endloop +endfacet +facet normal -0.227084527668728 -0.973875052197904 0.0 + outer loop + vertex 3.86888189176899 0.598077766287549 0.0 + vertex 3.91390104885711 0.588020295023182 0.2 + vertex 3.86888189176899 0.598077766287549 0.2 + endloop +endfacet +facet normal -0.227084527668728 -0.973875052197904 0.0 + outer loop + vertex 3.86888189176899 0.598077766287549 0.2 + vertex 3.81763668104102 0.610529873567243 0.0 + vertex 3.86888189176899 0.598077766287549 0.0 + endloop +endfacet +facet normal -0.315271954457167 -0.949001367086875 0.0 + outer loop + vertex 3.81763668104102 0.610529873567243 0.0 + vertex 3.86888189176899 0.598077766287549 0.2 + vertex 3.81763668104102 0.610529873567243 0.2 + endloop +endfacet +facet normal -0.315271954457167 -0.949001367086875 0.0 + outer loop + vertex 3.81763668104102 0.610529873567243 0.2 + vertex 3.76471522510232 0.633099318011688 0.0 + vertex 3.81763668104102 0.610529873567243 0.0 + endloop +endfacet +facet normal -0.53613649635636 -0.844131303337773 0.0 + outer loop + vertex 3.76471522510232 0.633099318011688 0.0 + vertex 3.81763668104102 0.610529873567243 0.2 + vertex 3.76471522510232 0.633099318011688 0.2 + endloop +endfacet +facet normal -0.53613649635636 -0.844131303337773 0.0 + outer loop + vertex 3.76471522510232 0.633099318011688 0.2 + vertex 3.72855622127091 0.665367038318201 0.0 + vertex 3.76471522510232 0.633099318011688 0.0 + endloop +endfacet +facet normal -0.792360540300353 -0.610053091275614 0.0 + outer loop + vertex 3.72855622127091 0.665367038318201 0.0 + vertex 3.76471522510232 0.633099318011688 0.2 + vertex 3.72855622127091 0.665367038318201 0.2 + endloop +endfacet +facet normal -0.953631698966797 -0.300976050086549 0.0 + outer loop + vertex 3.70772288793757 0.706375180080653 0.2 + vertex 3.72855622127091 0.665367038318201 0.0 + vertex 3.72855622127091 0.665367038318201 0.2 + endloop +endfacet +facet normal -0.792360540300353 -0.610053091275614 0.0 + outer loop + vertex 3.72855622127091 0.665367038318201 0.0 + vertex 3.70772288793757 0.706375180080653 0.2 + vertex 3.70772288793757 0.706375180080653 0.0 + endloop +endfacet +facet normal -0.999771622308441 -0.0213706159655532 0.0 + outer loop + vertex 3.70077844349313 0.755165888892914 0.2 + vertex 3.70772288793757 0.706375180080653 0.0 + vertex 3.70772288793757 0.706375180080653 0.2 + endloop +endfacet +facet normal -0.953631698966797 -0.300976050086549 0.0 + outer loop + vertex 3.70772288793757 0.706375180080653 0.0 + vertex 3.70077844349313 0.755165888892914 0.2 + vertex 3.70077844349313 0.755165888892914 0.0 + endloop +endfacet +facet normal -0.978410359122527 0.206671645761406 0.0 + outer loop + vertex 3.70419079981497 0.789648647513603 0.2 + vertex 3.70077844349313 0.755165888892914 0.0 + vertex 3.70077844349313 0.755165888892914 0.2 + endloop +endfacet +facet normal -0.999771622308441 -0.0213706159655532 0.0 + outer loop + vertex 3.70077844349313 0.755165888892914 0.0 + vertex 3.70419079981497 0.789648647513603 0.2 + vertex 3.70419079981497 0.789648647513603 0.0 + endloop +endfacet +facet normal -0.907181743274203 0.420738974507922 0.0 + outer loop + vertex 3.71442786878049 0.820778915712837 0.2 + vertex 3.70419079981497 0.789648647513603 0.0 + vertex 3.70419079981497 0.789648647513603 0.2 + endloop +endfacet +facet normal -0.978410359122527 0.206671645761406 0.0 + outer loop + vertex 3.70419079981497 0.789648647513603 0.0 + vertex 3.71442786878049 0.820778915712837 0.2 + vertex 3.71442786878049 0.820778915712837 0.0 + endloop +endfacet +facet normal -0.788438815616603 0.615113187981764 0.0 + outer loop + vertex 3.73148965038968 0.848556693490615 0.2 + vertex 3.71442786878049 0.820778915712837 0.0 + vertex 3.71442786878049 0.820778915712837 0.2 + endloop +endfacet +facet normal -0.907181743274203 0.420738974507922 0.0 + outer loop + vertex 3.71442786878049 0.820778915712837 0.0 + vertex 3.73148965038968 0.848556693490615 0.2 + vertex 3.73148965038968 0.848556693490615 0.0 + endloop +endfacet +facet normal -0.640172912233049 0.768230852311372 0.0 + outer loop + vertex 3.75537614464255 0.872981980846936 0.2 + vertex 3.73148965038968 0.848556693490615 0.0 + vertex 3.73148965038968 0.848556693490615 0.2 + endloop +endfacet +facet normal -0.788438815616603 0.615113187981764 0.0 + outer loop + vertex 3.73148965038968 0.848556693490615 0.0 + vertex 3.75537614464255 0.872981980846936 0.2 + vertex 3.75537614464255 0.872981980846936 0.0 + endloop +endfacet +facet normal -0.640172912233049 0.768230852311372 0.0 + outer loop + vertex 3.75537614464255 0.872981980846936 0.2 + vertex 3.78491996648164 0.892887392724331 0.0 + vertex 3.75537614464255 0.872981980846936 0.0 + endloop +endfacet +facet normal -0.474426136288194 0.880295314770365 0.0 + outer loop + vertex 3.78491996648164 0.892887392724331 0.0 + vertex 3.75537614464255 0.872981980846936 0.2 + vertex 3.78491996648164 0.892887392724331 0.2 + endloop +endfacet +facet normal -0.474426136288194 0.880295314770365 0.0 + outer loop + vertex 3.78491996648164 0.892887392724331 0.2 + vertex 3.81895373084945 0.907105544065327 0.0 + vertex 3.78491996648164 0.892887392724331 0.0 + endloop +endfacet +facet normal -0.302036254972224 0.953296439037907 0.0 + outer loop + vertex 3.81895373084945 0.907105544065327 0.0 + vertex 3.78491996648164 0.892887392724331 0.2 + vertex 3.81895373084945 0.907105544065327 0.2 + endloop +endfacet +facet normal -0.302036254972224 0.953296439037907 0.0 + outer loop + vertex 3.81895373084945 0.907105544065327 0.2 + vertex 3.857477437746 0.915636434869925 0.0 + vertex 3.81895373084945 0.907105544065327 0.0 + endloop +endfacet +facet normal -0.141494696370883 0.989939013727064 0.0 + outer loop + vertex 3.857477437746 0.915636434869925 0.0 + vertex 3.81895373084945 0.907105544065327 0.2 + vertex 3.857477437746 0.915636434869925 0.2 + endloop +endfacet +facet normal -0.141494696370883 0.989939013727064 0.0 + outer loop + vertex 3.857477437746 0.915636434869925 0.2 + vertex 3.90049108717129 0.918480065138124 0.0 + vertex 3.857477437746 0.915636434869925 0.0 + endloop +endfacet +facet normal 0.0281793303801236 0.999602883819034 0.0 + outer loop + vertex 3.90049108717129 0.918480065138124 0.0 + vertex 3.857477437746 0.915636434869925 0.2 + vertex 3.90049108717129 0.918480065138124 0.2 + endloop +endfacet +facet normal 0.0281793303801236 0.999602883819034 0.0 + outer loop + vertex 3.90049108717129 0.918480065138124 0.2 + vertex 3.99005047414447 0.907464739467626 0.0 + vertex 3.90049108717129 0.918480065138124 0.0 + endloop +endfacet +facet normal 0.234883143477802 0.972023615407561 0.0 + outer loop + vertex 3.99005047414447 0.907464739467626 0.0 + vertex 3.90049108717129 0.918480065138124 0.2 + vertex 3.99005047414447 0.907464739467626 0.2 + endloop +endfacet +facet normal 0.234883143477802 0.972023615407561 0.0 + outer loop + vertex 3.99005047414447 0.907464739467626 0.2 + vertex 4.06572097222876 0.879686961689848 0.0 + vertex 3.99005047414447 0.907464739467626 0.0 + endloop +endfacet +facet normal 0.344603779779219 0.938748227674426 0.0 + outer loop + vertex 4.06572097222876 0.879686961689848 0.0 + vertex 3.99005047414447 0.907464739467626 0.2 + vertex 4.06572097222876 0.879686961689848 0.2 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.06572097222876 0.879686961689848 0.0 + vertex 4.06572097222876 0.783422593873756 0.2 + vertex 4.06572097222876 0.783422593873756 0.0 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.06572097222876 0.783422593873756 0.2 + vertex 4.06572097222876 0.879686961689848 0.0 + vertex 4.06572097222876 0.879686961689848 0.2 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.06572097222876 0.783422593873756 0.2 + vertex 4.06093170019811 0.783422593873756 0.0 + vertex 4.06572097222876 0.783422593873756 0.0 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.06093170019811 0.783422593873756 0.0 + vertex 4.06572097222876 0.783422593873756 0.2 + vertex 4.06093170019811 0.783422593873756 0.2 + endloop +endfacet +facet normal -0.556587638965561 -0.830788902279479 0.0 + outer loop + vertex 4.06093170019811 0.783422593873756 0.2 + vertex 4.02447336686478 0.807847881230078 0.0 + vertex 4.06093170019811 0.783422593873756 0.0 + endloop +endfacet +facet normal -0.489592222603488 -0.871951521339447 0.0 + outer loop + vertex 4.02447336686478 0.807847881230078 0.0 + vertex 4.06093170019811 0.783422593873756 0.2 + vertex 4.02447336686478 0.807847881230078 0.2 + endloop +endfacet +facet normal -0.489592222603488 -0.871951521339447 0.0 + outer loop + vertex 4.02447336686478 0.807847881230078 0.2 + vertex 3.98406388410616 0.826526042149619 0.0 + vertex 4.02447336686478 0.807847881230078 0.0 + endloop +endfacet +facet normal -0.345043802382217 -0.938586583346268 0.0 + outer loop + vertex 3.98406388410616 0.826526042149619 0.0 + vertex 4.02447336686478 0.807847881230078 0.2 + vertex 3.98406388410616 0.826526042149619 0.2 + endloop +endfacet +facet normal -0.345043802382217 -0.938586583346268 0.0 + outer loop + vertex 3.98406388410616 0.826526042149619 0.2 + vertex 3.94149922893374 0.838379490425481 0.0 + vertex 3.98406388410616 0.826526042149619 0.0 + endloop +endfacet +facet normal -0.180697484689396 -0.983538722688093 0.0 + outer loop + vertex 3.94149922893374 0.838379490425481 0.0 + vertex 3.98406388410616 0.826526042149619 0.2 + vertex 3.94149922893374 0.838379490425481 0.2 + endloop +endfacet +facet normal -0.180697484689396 -0.983538722688093 0.0 + outer loop + vertex 3.94149922893374 0.838379490425481 0.2 + vertex 3.89857537835903 0.842330639850768 0.0 + vertex 3.94149922893374 0.838379490425481 0.0 + endloop +endfacet +facet normal 0.0107122258632747 -0.999942622462436 0.0 + outer loop + vertex 3.89857537835903 0.842330639850768 0.0 + vertex 3.94149922893374 0.838379490425481 0.2 + vertex 3.89857537835903 0.842330639850768 0.2 + endloop +endfacet +facet normal 0.0107122258632747 -0.999942622462436 0.0 + outer loop + vertex 3.89857537835903 0.842330639850768 0.2 + vertex 3.85750737069619 0.837661099620883 0.0 + vertex 3.89857537835903 0.842330639850768 0.0 + endloop +endfacet +facet normal 0.24874281702269 -0.968569569509396 0.0 + outer loop + vertex 3.85750737069619 0.837661099620883 0.0 + vertex 3.89857537835903 0.842330639850768 0.2 + vertex 3.85750737069619 0.837661099620883 0.2 + endloop +endfacet +facet normal 0.24874281702269 -0.968569569509396 0.0 + outer loop + vertex 3.85750737069619 0.837661099620883 0.2 + vertex 3.8233838074778 0.823652478931228 0.0 + vertex 3.85750737069619 0.837661099620883 0.0 + endloop +endfacet +facet normal 0.558542313018932 -0.829476030134362 0.0 + outer loop + vertex 3.8233838074778 0.823652478931228 0.0 + vertex 3.85750737069619 0.837661099620883 0.2 + vertex 3.8233838074778 0.823652478931228 0.2 + endloop +endfacet +facet normal 0.558542313018932 -0.829476030134362 0.0 + outer loop + vertex 3.8233838074778 0.823652478931228 0.0 + vertex 3.80039530173068 0.800185045981036 0.2 + vertex 3.80039530173068 0.800185045981036 0.0 + endloop +endfacet +facet normal 0.714358581375118 -0.699779834816444 0.0 + outer loop + vertex 3.80039530173068 0.800185045981036 0.2 + vertex 3.8233838074778 0.823652478931228 0.0 + vertex 3.8233838074778 0.823652478931228 0.2 + endloop +endfacet +facet normal 0.974152703703863 -0.225890482018285 0.0 + outer loop + vertex 3.80039530173068 0.800185045981036 0.0 + vertex 3.79273246648164 0.767139068969542 0.2 + vertex 3.79273246648164 0.767139068969542 0.0 + endloop +endfacet +facet normal 0.99932164254674 -0.0368273639796066 0.0 + outer loop + vertex 3.79273246648164 0.767139068969542 0.2 + vertex 3.80039530173068 0.800185045981036 0.0 + vertex 3.80039530173068 0.800185045981036 0.2 + endloop +endfacet +facet normal 0.99932164254674 -0.0368273639796066 0.0 + outer loop + vertex 3.79273246648164 0.767139068969542 0.0 + vertex 3.79728227491075 0.737864643682186 0.2 + vertex 3.79728227491075 0.737864643682186 0.0 + endloop +endfacet +facet normal 0.933137120913151 0.359520671970215 0.0 + outer loop + vertex 3.79728227491075 0.737864643682186 0.2 + vertex 3.79273246648164 0.767139068969542 0.0 + vertex 3.79273246648164 0.767139068969542 0.2 + endloop +endfacet +facet normal 0.933137120913151 0.359520671970215 0.0 + outer loop + vertex 3.79728227491075 0.737864643682186 0.0 + vertex 3.81093170019811 0.717091176249235 0.2 + vertex 3.81093170019811 0.717091176249235 0.0 + endloop +endfacet +facet normal 0.701287328298952 0.712878729635916 0.0 + outer loop + vertex 3.81093170019811 0.717091176249235 0.2 + vertex 3.79728227491075 0.737864643682186 0.0 + vertex 3.79728227491075 0.737864643682186 0.2 + endloop +endfacet +facet normal 0.701287328298952 0.712878729635916 0.0 + outer loop + vertex 3.81093170019811 0.717091176249235 0.2 + vertex 3.83463859674983 0.702064835253067 0.0 + vertex 3.81093170019811 0.717091176249235 0.0 + endloop +endfacet +facet normal 0.434303289715712 0.900766702616227 0.0 + outer loop + vertex 3.83463859674983 0.702064835253067 0.0 + vertex 3.81093170019811 0.717091176249235 0.2 + vertex 3.83463859674983 0.702064835253067 0.2 + endloop +endfacet +facet normal 0.434303289715712 0.900766702616227 0.0 + outer loop + vertex 3.83463859674983 0.702064835253067 0.2 + vertex 3.86936081897206 0.690031789276055 0.0 + vertex 3.83463859674983 0.702064835253067 0.0 + endloop +endfacet +facet normal 0.27617167833275 0.961108320683404 0.0 + outer loop + vertex 3.86936081897206 0.690031789276055 0.0 + vertex 3.83463859674983 0.702064835253067 0.2 + vertex 3.86936081897206 0.690031789276055 0.2 + endloop +endfacet +facet normal 0.27617167833275 0.961108320683404 0.0 + outer loop + vertex 3.86936081897206 0.690031789276055 0.2 + vertex 3.91414051245865 0.679734854410155 0.0 + vertex 3.86936081897206 0.690031789276055 0.0 + endloop +endfacet +facet normal 0.222179963498852 0.975005673737158 0.0 + outer loop + vertex 3.91414051245865 0.679734854410155 0.0 + vertex 3.86936081897206 0.690031789276055 0.2 + vertex 3.91414051245865 0.679734854410155 0.2 + endloop +endfacet +facet normal 0.222179963498852 0.975005673737158 0.0 + outer loop + vertex 3.91414051245865 0.679734854410155 0.2 + vertex 3.96610411399121 0.668001137935059 0.0 + vertex 3.91414051245865 0.679734854410155 0.0 + endloop +endfacet +facet normal 0.288350612204699 0.95752489494591 0.0 + outer loop + vertex 3.96610411399121 0.668001137935059 0.0 + vertex 3.91414051245865 0.679734854410155 0.2 + vertex 3.96610411399121 0.668001137935059 0.2 + endloop +endfacet +facet normal 0.288350612204699 0.95752489494591 0.0 + outer loop + vertex 3.96610411399121 0.668001137935059 0.2 + vertex 4.01812758142416 0.648245390808622 0.0 + vertex 3.96610411399121 0.668001137935059 0.0 + endloop +endfacet +facet normal 0.493989893056549 0.869467644917267 0.0 + outer loop + vertex 4.01812758142416 0.648245390808622 0.0 + vertex 3.96610411399121 0.668001137935059 0.2 + vertex 4.01812758142416 0.648245390808622 0.2 + endloop +endfacet +facet normal 0.493989893056549 0.869467644917267 0.0 + outer loop + vertex 4.01812758142416 0.648245390808622 0.2 + vertex 4.05542403736286 0.61867163601935 0.0 + vertex 4.01812758142416 0.648245390808622 0.0 + endloop +endfacet +facet normal 0.763439764736457 0.645879033271048 0.0 + outer loop + vertex 4.05542403736286 0.61867163601935 0.0 + vertex 4.01812758142416 0.648245390808622 0.2 + vertex 4.05542403736286 0.61867163601935 0.2 + endloop +endfacet +facet normal 0.763439764736457 0.645879033271048 0.0 + outer loop + vertex 4.05542403736286 0.61867163601935 0.0 + vertex 4.07787375000654 0.577962823758814 0.2 + vertex 4.07787375000654 0.577962823758814 0.0 + endloop +endfacet +facet normal 0.948633107227256 0.316378298674802 0.0 + outer loop + vertex 4.07787375000654 0.577962823758814 0.2 + vertex 4.05542403736286 0.61867163601935 0.0 + vertex 4.05542403736286 0.61867163601935 0.2 + endloop +endfacet +facet normal 0.948633107227256 0.316378298674802 0.0 + outer loop + vertex 4.07787375000654 0.577962823758814 0.0 + vertex 4.08535698755443 0.524801904218584 0.2 + vertex 4.08535698755443 0.524801904218584 0.0 + endloop +endfacet +facet normal 0.999813856156834 0.0192938600803773 0.0 + outer loop + vertex 4.08535698755443 0.524801904218584 0.2 + vertex 4.07787375000654 0.577962823758814 0.0 + vertex 4.07787375000654 0.577962823758814 0.2 + endloop +endfacet +facet normal 0.999813856156834 0.0192938600803773 0.0 + outer loop + vertex 4.08535698755443 0.524801904218584 0.0 + vertex 4.08194463123259 0.491217134103642 0.2 + vertex 4.08194463123259 0.491217134103642 0.0 + endloop +endfacet +facet normal 0.978774660177288 -0.204939416889078 0.0 + outer loop + vertex 4.08194463123259 0.491217134103642 0.2 + vertex 4.08535698755443 0.524801904218584 0.0 + vertex 4.08535698755443 0.524801904218584 0.2 + endloop +endfacet +facet normal 0.978774660177288 -0.204939416889078 0.0 + outer loop + vertex 4.08194463123259 0.491217134103642 0.0 + vertex 4.07170756226708 0.459428341000193 0.2 + vertex 4.07170756226708 0.459428341000193 0.0 + endloop +endfacet +facet normal 0.912663557218166 -0.408711672607822 0.0 + outer loop + vertex 4.07170756226708 0.459428341000193 0.2 + vertex 4.08194463123259 0.491217134103642 0.0 + vertex 4.08194463123259 0.491217134103642 0.2 + endloop +endfacet +facet normal 0.912663557218166 -0.408711672607822 0.0 + outer loop + vertex 4.07170756226708 0.459428341000193 0.0 + vertex 4.05488524425941 0.430752574716668 0.2 + vertex 4.05488524425941 0.430752574716668 0.0 + endloop +endfacet +facet normal 0.798133217875601 -0.60248100926381 0.0 + outer loop + vertex 4.05488524425941 0.430752574716668 0.2 + vertex 4.07170756226708 0.459428341000193 0.0 + vertex 4.07170756226708 0.459428341000193 0.2 + endloop +endfacet +facet normal 0.798133217875601 -0.60248100926381 0.0 + outer loop + vertex 4.05488524425941 0.430752574716668 0.0 + vertex 4.03171714081114 0.406506885061496 0.2 + vertex 4.03171714081114 0.406506885061496 0.0 + endloop +endfacet +facet normal 0.648537069293553 -0.761183072428787 0.0 + outer loop + vertex 4.03171714081114 0.406506885061496 0.2 + vertex 4.05488524425941 0.430752574716668 0.0 + vertex 4.05488524425941 0.430752574716668 0.2 + endloop +endfacet +facet normal 0.648537069293553 -0.761183072428787 0.0 + outer loop + vertex 4.03171714081114 0.406506885061496 0.2 + vertex 4.00112566571535 0.38543408812663 0.0 + vertex 4.03171714081114 0.406506885061496 0.0 + endloop +endfacet +facet normal 0.489645436592736 -0.871921640070889 0.0 + outer loop + vertex 4.00112566571535 0.38543408812663 0.0 + vertex 4.03171714081114 0.406506885061496 0.2 + vertex 4.00112566571535 0.38543408812663 0.2 + endloop +endfacet +facet normal 0.489645436592736 -0.871921640070889 0.0 + outer loop + vertex 4.00112566571535 0.38543408812663 0.2 + vertex 3.96682250479581 0.370108417628546 0.0 + vertex 4.00112566571535 0.38543408812663 0.0 + endloop +endfacet +facet normal 0.317107169365693 -0.948389710581509 0.0 + outer loop + vertex 3.96682250479581 0.370108417628546 0.0 + vertex 4.00112566571535 0.38543408812663 0.2 + vertex 3.96682250479581 0.370108417628546 0.2 + endloop +endfacet +facet normal 0.317107169365693 -0.948389710581509 0.0 + outer loop + vertex 3.96682250479581 0.370108417628546 0.2 + vertex 3.92605382663489 0.360769337168776 0.0 + vertex 3.96682250479581 0.370108417628546 0.0 + endloop +endfacet +facet normal 0.143198383976245 -0.989694004643148 0.0 + outer loop + vertex 3.92605382663489 0.360769337168776 0.0 + vertex 3.96682250479581 0.370108417628546 0.2 + vertex 3.92605382663489 0.360769337168776 0.2 + endloop +endfacet +facet normal 0.143198383976245 -0.989694004643148 0.0 + outer loop + vertex 3.92605382663489 0.360769337168776 0.2 + vertex 3.87606579981497 0.357656310348852 0.0 + vertex 3.92605382663489 0.360769337168776 0.0 + endloop +endfacet +facet normal -0.0034494236932481 -0.999994050720395 0.0 + outer loop + vertex 3.87606579981497 0.357656310348852 0.0 + vertex 3.92605382663489 0.360769337168776 0.2 + vertex 3.87606579981497 0.357656310348852 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.13684166188393 0.571078245214753 0.2 + vertex 4.13702125958508 0.702962823758814 0.2 + vertex 4.13229185345481 0.63734979693889 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.13702125958508 0.702962823758814 0.2 + vertex 4.13684166188393 0.571078245214753 0.2 + vertex 4.15049108717129 0.514026042149619 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.13702125958508 0.702962823758814 0.2 + vertex 4.15049108717129 0.514026042149619 0.2 + vertex 4.15120947797589 0.758997306517435 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.15120947797589 0.758997306517435 0.2 + vertex 4.15049108717129 0.514026042149619 0.2 + vertex 4.17240200671152 0.466073455942722 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.15120947797589 0.758997306517435 0.2 + vertex 4.17240200671152 0.466073455942722 0.2 + vertex 4.17329999521727 0.80617163601935 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.17329999521727 0.80617163601935 0.2 + vertex 4.17240200671152 0.466073455942722 0.2 + vertex 4.20173629789926 0.427100754793297 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.17329999521727 0.80617163601935 0.2 + vertex 4.20173629789926 0.427100754793297 0.2 + vertex 4.20173629789926 0.845204203069159 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.20173629789926 0.845204203069159 0.2 + vertex 4.20173629789926 0.427100754793297 0.2 + vertex 4.23801503353144 0.396988206900576 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.20173629789926 0.845204203069159 0.2 + vertex 4.23801503353144 0.396988206900576 0.2 + vertex 4.22568265805252 0.63734979693889 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.22568265805252 0.63734979693889 0.2 + vertex 4.23801503353144 0.396988206900576 0.2 + vertex 4.2282419252939 0.592555136977205 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.2282419252939 0.592555136977205 0.2 + vertex 4.23801503353144 0.396988206900576 0.2 + vertex 4.23591972701803 0.552998743299044 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.23591972701803 0.552998743299044 0.2 + vertex 4.23801503353144 0.396988206900576 0.2 + vertex 4.24871606322493 0.518680615904408 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.24871606322493 0.518680615904408 0.2 + vertex 4.23801503353144 0.396988206900576 0.2 + vertex 4.28075928640501 0.375616080463795 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.24871606322493 0.518680615904408 0.2 + vertex 4.28075928640501 0.375616080463795 0.2 + vertex 4.26663093391458 0.489600754793297 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.26663093391458 0.489600754793297 0.2 + vertex 4.28075928640501 0.375616080463795 0.2 + vertex 4.28909561303336 0.466447617820117 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.28909561303336 0.466447617820117 0.2 + vertex 4.28075928640501 0.375616080463795 0.2 + vertex 4.32901120211382 0.362864643682186 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.28909561303336 0.466447617820117 0.2 + vertex 4.32901120211382 0.362864643682186 0.2 + vertex 4.31554137452761 0.449909662839274 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.31554137452761 0.449909662839274 0.2 + vertex 4.32901120211382 0.362864643682186 0.2 + vertex 4.34596821839734 0.439986889850768 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.34596821839734 0.439986889850768 0.2 + vertex 4.32901120211382 0.362864643682186 0.2 + vertex 4.38181292625175 0.358614164754983 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.34596821839734 0.439986889850768 0.2 + vertex 4.38181292625175 0.358614164754983 0.2 + vertex 4.38037614464255 0.436679298854599 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.38037614464255 0.436679298854599 0.2 + vertex 4.38181292625175 0.358614164754983 0.2 + vertex 4.42335986111765 0.441229107283718 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.42335986111765 0.441229107283718 0.2 + vertex 4.38181292625175 0.358614164754983 0.2 + vertex 4.42874779215213 0.362206118777971 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.42335986111765 0.441229107283718 0.2 + vertex 4.42874779215213 0.362206118777971 0.2 + vertex 4.46418840517895 0.454878532571074 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.46418840517895 0.454878532571074 0.2 + vertex 4.42874779215213 0.362206118777971 0.2 + vertex 4.46993553161574 0.372503053643871 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.46418840517895 0.454878532571074 0.2 + vertex 4.46993553161574 0.372503053643871 0.2 + vertex 4.5016644588188 0.475711865904408 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.5016644588188 0.475711865904408 0.2 + vertex 4.46993553161574 0.372503053643871 0.2 + vertex 4.50633399904869 0.385673551728163 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.5016644588188 0.475711865904408 0.2 + vertex 4.50633399904869 0.385673551728163 0.2 + vertex 4.53459070402953 0.501813398471458 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.53459070402953 0.501813398471458 0.2 + vertex 4.50633399904869 0.385673551728163 0.2 + vertex 4.53985890326324 0.400759758624714 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.53459070402953 0.501813398471458 0.2 + vertex 4.53985890326324 0.400759758624714 0.2 + vertex 4.53985890326324 0.501813398471458 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.22568265805252 0.63734979693889 0.2 + vertex 4.23723677682646 0.876154873567243 0.2 + vertex 4.20173629789926 0.845204203069159 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.23723677682646 0.876154873567243 0.2 + vertex 4.22568265805252 0.63734979693889 0.2 + vertex 4.22833172414447 0.68298257950594 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.23723677682646 0.876154873567243 0.2 + vertex 4.22833172414447 0.68298257950594 0.2 + vertex 4.23627892242033 0.723137632187933 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.23723677682646 0.876154873567243 0.2 + vertex 4.23627892242033 0.723137632187933 0.2 + vertex 4.2495242528801 0.757814954984868 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.23723677682646 0.876154873567243 0.2 + vertex 4.2495242528801 0.757814954984868 0.2 + vertex 4.28051982280347 0.899083513413986 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.28051982280347 0.899083513413986 0.2 + vertex 4.2495242528801 0.757814954984868 0.2 + vertex 4.26806771552378 0.787014547896745 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.28051982280347 0.899083513413986 0.2 + vertex 4.26806771552378 0.787014547896745 0.2 + vertex 4.29098138889543 0.810167684869925 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.28051982280347 0.899083513413986 0.2 + vertex 4.29098138889543 0.810167684869925 0.2 + vertex 4.3294302634165 0.913271731804791 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.3294302634165 0.913271731804791 0.2 + vertex 4.29098138889543 0.810167684869925 0.2 + vertex 4.31733735153911 0.826705639850768 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.3294302634165 0.913271731804791 0.2 + vertex 4.31733735153911 0.826705639850768 0.2 + vertex 4.34713560345482 0.836628412839274 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.3294302634165 0.913271731804791 0.2 + vertex 4.34713560345482 0.836628412839274 0.2 + vertex 4.38181292625175 0.918001137935059 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.38181292625175 0.918001137935059 0.2 + vertex 4.34713560345482 0.836628412839274 0.2 + vertex 4.38037614464255 0.839936003835442 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.38181292625175 0.918001137935059 0.2 + vertex 4.38037614464255 0.839936003835442 0.2 + vertex 4.4292267193552 0.832752095789465 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.38181292625175 0.918001137935059 0.2 + vertex 4.4292267193552 0.832752095789465 0.2 + vertex 4.42509597222876 0.914888111115136 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.42509597222876 0.914888111115136 0.2 + vertex 4.4292267193552 0.832752095789465 0.2 + vertex 4.46682250479581 0.905549030655366 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.46682250479581 0.905549030655366 0.2 + vertex 4.4292267193552 0.832752095789465 0.2 + vertex 4.47328802203719 0.815989643682186 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.46682250479581 0.905549030655366 0.2 + vertex 4.47328802203719 0.815989643682186 0.2 + vertex 4.53985890326324 0.876813398471458 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.53985890326324 0.876813398471458 0.2 + vertex 4.47328802203719 0.815989643682186 0.2 + vertex 4.50992595307167 0.794437919544255 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.53985890326324 0.876813398471458 0.2 + vertex 4.50992595307167 0.794437919544255 0.2 + vertex 4.53459070402953 0.775759758624714 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.53985890326324 0.876813398471458 0.2 + vertex 4.53459070402953 0.775759758624714 0.2 + vertex 4.53985890326324 0.775759758624714 0.2 + endloop +endfacet +facet normal -0.00197196685745208 -0.999998055671466 0.0 + outer loop + vertex 4.38181292625175 0.358614164754983 0.2 + vertex 4.32901120211382 0.362864643682186 0.0 + vertex 4.38181292625175 0.358614164754983 0.0 + endloop +endfacet +facet normal -0.168535418585088 -0.985695598388442 0.0 + outer loop + vertex 4.32901120211382 0.362864643682186 0.0 + vertex 4.38181292625175 0.358614164754983 0.2 + vertex 4.32901120211382 0.362864643682186 0.2 + endloop +endfacet +facet normal -0.168535418585088 -0.985695598388442 0.0 + outer loop + vertex 4.32901120211382 0.362864643682186 0.2 + vertex 4.28075928640501 0.375616080463795 0.0 + vertex 4.32901120211382 0.362864643682186 0.0 + endloop +endfacet +facet normal -0.35321427508086 -0.935542450067929 0.0 + outer loop + vertex 4.28075928640501 0.375616080463795 0.0 + vertex 4.32901120211382 0.362864643682186 0.2 + vertex 4.28075928640501 0.375616080463795 0.2 + endloop +endfacet +facet normal -0.35321427508086 -0.935542450067929 0.0 + outer loop + vertex 4.28075928640501 0.375616080463795 0.2 + vertex 4.23801503353144 0.396988206900576 0.0 + vertex 4.28075928640501 0.375616080463795 0.0 + endloop +endfacet +facet normal -0.54653206616668 -0.837438177211655 0.0 + outer loop + vertex 4.23801503353144 0.396988206900576 0.0 + vertex 4.28075928640501 0.375616080463795 0.2 + vertex 4.23801503353144 0.396988206900576 0.2 + endloop +endfacet +facet normal -0.54653206616668 -0.837438177211655 0.0 + outer loop + vertex 4.23801503353144 0.396988206900576 0.2 + vertex 4.20173629789926 0.427100754793297 0.0 + vertex 4.23801503353144 0.396988206900576 0.0 + endloop +endfacet +facet normal -0.723723054552945 -0.690090530516507 0.0 + outer loop + vertex 4.20173629789926 0.427100754793297 0.0 + vertex 4.23801503353144 0.396988206900576 0.2 + vertex 4.20173629789926 0.427100754793297 0.2 + endloop +endfacet +facet normal -0.859293185167977 -0.51148335449345 0.0 + outer loop + vertex 4.17240200671152 0.466073455942722 0.2 + vertex 4.20173629789926 0.427100754793297 0.0 + vertex 4.20173629789926 0.427100754793297 0.2 + endloop +endfacet +facet normal -0.723723054552945 -0.690090530516507 0.0 + outer loop + vertex 4.20173629789926 0.427100754793297 0.0 + vertex 4.17240200671152 0.466073455942722 0.2 + vertex 4.17240200671152 0.466073455942722 0.0 + endloop +endfacet +facet normal -0.945484976783479 -0.325665716151923 0.0 + outer loop + vertex 4.15049108717129 0.514026042149619 0.2 + vertex 4.17240200671152 0.466073455942722 0.0 + vertex 4.17240200671152 0.466073455942722 0.2 + endloop +endfacet +facet normal -0.859293185167977 -0.51148335449345 0.0 + outer loop + vertex 4.17240200671152 0.466073455942722 0.0 + vertex 4.15049108717129 0.514026042149619 0.2 + vertex 4.15049108717129 0.514026042149619 0.0 + endloop +endfacet +facet normal -0.988517345545667 -0.151107437127855 0.0 + outer loop + vertex 4.13684166188393 0.571078245214753 0.2 + vertex 4.15049108717129 0.514026042149619 0.0 + vertex 4.15049108717129 0.514026042149619 0.2 + endloop +endfacet +facet normal -0.945484976783479 -0.325665716151923 0.0 + outer loop + vertex 4.15049108717129 0.514026042149619 0.0 + vertex 4.13684166188393 0.571078245214753 0.2 + vertex 4.13684166188393 0.571078245214753 0.0 + endloop +endfacet +facet normal -0.999998547010283 0.00170469273558899 0.0 + outer loop + vertex 4.13229185345481 0.63734979693889 0.2 + vertex 4.13684166188393 0.571078245214753 0.0 + vertex 4.13684166188393 0.571078245214753 0.2 + endloop +endfacet +facet normal -0.988517345545667 -0.151107437127855 0.0 + outer loop + vertex 4.13684166188393 0.571078245214753 0.0 + vertex 4.13229185345481 0.63734979693889 0.2 + vertex 4.13229185345481 0.63734979693889 0.0 + endloop +endfacet +facet normal -0.987231360085845 0.159292942916666 0.0 + outer loop + vertex 4.13702125958508 0.702962823758814 0.2 + vertex 4.13229185345481 0.63734979693889 0.0 + vertex 4.13229185345481 0.63734979693889 0.2 + endloop +endfacet +facet normal -0.999998547010283 0.00170469273558899 0.0 + outer loop + vertex 4.13229185345481 0.63734979693889 0.0 + vertex 4.13702125958508 0.702962823758814 0.2 + vertex 4.13702125958508 0.702962823758814 0.0 + endloop +endfacet +facet normal -0.941760169938163 0.336285269255199 0.0 + outer loop + vertex 4.15120947797589 0.758997306517435 0.2 + vertex 4.13702125958508 0.702962823758814 0.0 + vertex 4.13702125958508 0.702962823758814 0.2 + endloop +endfacet +facet normal -0.987231360085845 0.159292942916666 0.0 + outer loop + vertex 4.13702125958508 0.702962823758814 0.0 + vertex 4.15120947797589 0.758997306517435 0.2 + vertex 4.15120947797589 0.758997306517435 0.0 + endloop +endfacet +facet normal -0.860889261127337 0.508792374231009 0.0 + outer loop + vertex 4.17329999521727 0.80617163601935 0.2 + vertex 4.15120947797589 0.758997306517435 0.0 + vertex 4.15120947797589 0.758997306517435 0.2 + endloop +endfacet +facet normal -0.941760169938163 0.336285269255199 0.0 + outer loop + vertex 4.15120947797589 0.758997306517435 0.0 + vertex 4.17329999521727 0.80617163601935 0.2 + vertex 4.17329999521727 0.80617163601935 0.0 + endloop +endfacet +facet normal -0.737329141670924 0.675533668178588 0.0 + outer loop + vertex 4.20173629789926 0.845204203069159 0.2 + vertex 4.17329999521727 0.80617163601935 0.0 + vertex 4.17329999521727 0.80617163601935 0.2 + endloop +endfacet +facet normal -0.860889261127337 0.508792374231009 0.0 + outer loop + vertex 4.17329999521727 0.80617163601935 0.0 + vertex 4.20173629789926 0.845204203069159 0.2 + vertex 4.20173629789926 0.845204203069159 0.0 + endloop +endfacet +facet normal -0.737329141670924 0.675533668178588 0.0 + outer loop + vertex 4.20173629789926 0.845204203069159 0.2 + vertex 4.23723677682646 0.876154873567243 0.0 + vertex 4.20173629789926 0.845204203069159 0.0 + endloop +endfacet +facet normal -0.566370220686766 0.824150940737936 0.0 + outer loop + vertex 4.23723677682646 0.876154873567243 0.0 + vertex 4.20173629789926 0.845204203069159 0.2 + vertex 4.23723677682646 0.876154873567243 0.2 + endloop +endfacet +facet normal -0.566370220686766 0.824150940737936 0.0 + outer loop + vertex 4.23723677682646 0.876154873567243 0.2 + vertex 4.28051982280347 0.899083513413986 0.0 + vertex 4.23723677682646 0.876154873567243 0.0 + endloop +endfacet +facet normal -0.3753227198207 0.926894198917218 0.0 + outer loop + vertex 4.28051982280347 0.899083513413986 0.0 + vertex 4.23723677682646 0.876154873567243 0.2 + vertex 4.28051982280347 0.899083513413986 0.2 + endloop +endfacet +facet normal -0.3753227198207 0.926894198917218 0.0 + outer loop + vertex 4.28051982280347 0.899083513413986 0.2 + vertex 4.3294302634165 0.913271731804791 0.0 + vertex 4.28051982280347 0.899083513413986 0.0 + endloop +endfacet +facet normal -0.185115133291196 0.982716839901801 0.0 + outer loop + vertex 4.3294302634165 0.913271731804791 0.0 + vertex 4.28051982280347 0.899083513413986 0.2 + vertex 4.3294302634165 0.913271731804791 0.2 + endloop +endfacet +facet normal -0.185115133291196 0.982716839901801 0.0 + outer loop + vertex 4.3294302634165 0.913271731804791 0.2 + vertex 4.38181292625175 0.918001137935059 0.0 + vertex 4.3294302634165 0.913271731804791 0.0 + endloop +endfacet +facet normal -0.00912120952563407 0.999958400903152 0.0 + outer loop + vertex 4.38181292625175 0.918001137935059 0.0 + vertex 4.3294302634165 0.913271731804791 0.2 + vertex 4.38181292625175 0.918001137935059 0.2 + endloop +endfacet +facet normal -0.00912120952563407 0.999958400903152 0.0 + outer loop + vertex 4.38181292625175 0.918001137935059 0.2 + vertex 4.42509597222876 0.914888111115136 0.0 + vertex 4.38181292625175 0.918001137935059 0.0 + endloop +endfacet +facet normal 0.145475165786851 0.989361903521299 0.0 + outer loop + vertex 4.42509597222876 0.914888111115136 0.0 + vertex 4.38181292625175 0.918001137935059 0.2 + vertex 4.42509597222876 0.914888111115136 0.2 + endloop +endfacet +facet normal 0.145475165786851 0.989361903521299 0.0 + outer loop + vertex 4.42509597222876 0.914888111115136 0.2 + vertex 4.46682250479581 0.905549030655366 0.0 + vertex 4.42509597222876 0.914888111115136 0.0 + endloop +endfacet +facet normal 0.293144455107609 0.956068160979992 0.0 + outer loop + vertex 4.46682250479581 0.905549030655366 0.0 + vertex 4.42509597222876 0.914888111115136 0.2 + vertex 4.46682250479581 0.905549030655366 0.2 + endloop +endfacet +facet normal 0.293144455107609 0.956068160979992 0.0 + outer loop + vertex 4.46682250479581 0.905549030655366 0.2 + vertex 4.53985890326324 0.876813398471458 0.0 + vertex 4.46682250479581 0.905549030655366 0.0 + endloop +endfacet +facet normal 0.366124308864546 0.930565951697385 0.0 + outer loop + vertex 4.53985890326324 0.876813398471458 0.0 + vertex 4.46682250479581 0.905549030655366 0.2 + vertex 4.53985890326324 0.876813398471458 0.2 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.53985890326324 0.876813398471458 0.0 + vertex 4.53985890326324 0.775759758624714 0.2 + vertex 4.53985890326324 0.775759758624714 0.0 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.53985890326324 0.775759758624714 0.2 + vertex 4.53985890326324 0.876813398471458 0.0 + vertex 4.53985890326324 0.876813398471458 0.2 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.53985890326324 0.775759758624714 0.2 + vertex 4.53459070402953 0.775759758624714 0.0 + vertex 4.53985890326324 0.775759758624714 0.0 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.53459070402953 0.775759758624714 0.0 + vertex 4.53985890326324 0.775759758624714 0.2 + vertex 4.53459070402953 0.775759758624714 0.2 + endloop +endfacet +facet normal -0.603708660405246 -0.797205025919747 0.0 + outer loop + vertex 4.53459070402953 0.775759758624714 0.2 + vertex 4.50992595307167 0.794437919544255 0.0 + vertex 4.53459070402953 0.775759758624714 0.0 + endloop +endfacet +facet normal -0.556306638945698 -0.830977089614955 0.0 + outer loop + vertex 4.50992595307167 0.794437919544255 0.0 + vertex 4.53459070402953 0.775759758624714 0.2 + vertex 4.50992595307167 0.794437919544255 0.2 + endloop +endfacet +facet normal -0.556306638945698 -0.830977089614955 0.0 + outer loop + vertex 4.50992595307167 0.794437919544255 0.2 + vertex 4.47328802203719 0.815989643682186 0.0 + vertex 4.50992595307167 0.794437919544255 0.0 + endloop +endfacet +facet normal -0.43282617311797 -0.901477400639668 0.0 + outer loop + vertex 4.47328802203719 0.815989643682186 0.0 + vertex 4.50992595307167 0.794437919544255 0.2 + vertex 4.47328802203719 0.815989643682186 0.2 + endloop +endfacet +facet normal -0.43282617311797 -0.901477400639668 0.0 + outer loop + vertex 4.47328802203719 0.815989643682186 0.2 + vertex 4.4292267193552 0.832752095789465 0.0 + vertex 4.47328802203719 0.815989643682186 0.0 + endloop +endfacet +facet normal -0.252022420714891 -0.967721395576747 0.0 + outer loop + vertex 4.4292267193552 0.832752095789465 0.0 + vertex 4.47328802203719 0.815989643682186 0.2 + vertex 4.4292267193552 0.832752095789465 0.2 + endloop +endfacet +facet normal -0.252022420714891 -0.967721395576747 0.0 + outer loop + vertex 4.4292267193552 0.832752095789465 0.2 + vertex 4.38037614464255 0.839936003835442 0.0 + vertex 4.4292267193552 0.832752095789465 0.0 + endloop +endfacet +facet normal -0.0234148588877288 -0.999725834608303 0.0 + outer loop + vertex 4.38037614464255 0.839936003835442 0.0 + vertex 4.4292267193552 0.832752095789465 0.2 + vertex 4.38037614464255 0.839936003835442 0.2 + endloop +endfacet +facet normal -0.0234148588877288 -0.999725834608303 0.0 + outer loop + vertex 4.38037614464255 0.839936003835442 0.2 + vertex 4.34713560345482 0.836628412839274 0.0 + vertex 4.38037614464255 0.839936003835442 0.0 + endloop +endfacet +facet normal 0.208766711475163 -0.977965469830017 0.0 + outer loop + vertex 4.34713560345482 0.836628412839274 0.0 + vertex 4.38037614464255 0.839936003835442 0.2 + vertex 4.34713560345482 0.836628412839274 0.2 + endloop +endfacet +facet normal 0.208766711475163 -0.977965469830017 0.0 + outer loop + vertex 4.34713560345482 0.836628412839274 0.2 + vertex 4.31733735153911 0.826705639850768 0.0 + vertex 4.34713560345482 0.836628412839274 0.0 + endloop +endfacet +facet normal 0.426768496452843 -0.904360907179971 0.0 + outer loop + vertex 4.31733735153911 0.826705639850768 0.0 + vertex 4.34713560345482 0.836628412839274 0.2 + vertex 4.31733735153911 0.826705639850768 0.2 + endloop +endfacet +facet normal 0.426768496452843 -0.904360907179971 0.0 + outer loop + vertex 4.31733735153911 0.826705639850768 0.2 + vertex 4.29098138889543 0.810167684869925 0.0 + vertex 4.31733735153911 0.826705639850768 0.0 + endloop +endfacet +facet normal 0.625279843247521 -0.780400613549449 0.0 + outer loop + vertex 4.29098138889543 0.810167684869925 0.0 + vertex 4.31733735153911 0.826705639850768 0.2 + vertex 4.29098138889543 0.810167684869925 0.2 + endloop +endfacet +facet normal 0.625279843247521 -0.780400613549449 0.0 + outer loop + vertex 4.29098138889543 0.810167684869925 0.0 + vertex 4.26806771552378 0.787014547896745 0.2 + vertex 4.26806771552378 0.787014547896745 0.0 + endloop +endfacet +facet normal 0.781955163451592 -0.623334679246545 0.0 + outer loop + vertex 4.26806771552378 0.787014547896745 0.2 + vertex 4.29098138889543 0.810167684869925 0.0 + vertex 4.29098138889543 0.810167684869925 0.2 + endloop +endfacet +facet normal 0.781955163451592 -0.623334679246545 0.0 + outer loop + vertex 4.26806771552378 0.787014547896745 0.0 + vertex 4.2495242528801 0.757814954984868 0.2 + vertex 4.2495242528801 0.757814954984868 0.0 + endloop +endfacet +facet normal 0.89367395924274 -0.448716898023025 0.0 + outer loop + vertex 4.2495242528801 0.757814954984868 0.2 + vertex 4.26806771552378 0.787014547896745 0.0 + vertex 4.26806771552378 0.787014547896745 0.2 + endloop +endfacet +facet normal 0.89367395924274 -0.448716898023025 0.0 + outer loop + vertex 4.2495242528801 0.757814954984868 0.0 + vertex 4.23627892242033 0.723137632187933 0.2 + vertex 4.23627892242033 0.723137632187933 0.0 + endloop +endfacet +facet normal 0.961021452644306 -0.276473809894227 0.0 + outer loop + vertex 4.23627892242033 0.723137632187933 0.2 + vertex 4.2495242528801 0.757814954984868 0.0 + vertex 4.2495242528801 0.757814954984868 0.2 + endloop +endfacet +facet normal 0.961021452644306 -0.276473809894227 0.0 + outer loop + vertex 4.23627892242033 0.723137632187933 0.0 + vertex 4.22833172414447 0.68298257950594 0.2 + vertex 4.22833172414447 0.68298257950594 0.0 + endloop +endfacet +facet normal 0.991985893896409 -0.126348669603372 0.0 + outer loop + vertex 4.22833172414447 0.68298257950594 0.2 + vertex 4.23627892242033 0.723137632187933 0.0 + vertex 4.23627892242033 0.723137632187933 0.2 + endloop +endfacet +facet normal 0.991985893896409 -0.126348669603372 0.0 + outer loop + vertex 4.22833172414447 0.68298257950594 0.0 + vertex 4.22568265805252 0.63734979693889 0.2 + vertex 4.22568265805252 0.63734979693889 0.0 + endloop +endfacet +facet normal 0.999999895238739 -0.000457736289950238 0.0 + outer loop + vertex 4.22568265805252 0.63734979693889 0.2 + vertex 4.22833172414447 0.68298257950594 0.0 + vertex 4.22833172414447 0.68298257950594 0.2 + endloop +endfacet +facet normal 0.999999895238739 -0.000457736289950238 0.0 + outer loop + vertex 4.22568265805252 0.63734979693889 0.0 + vertex 4.2282419252939 0.592555136977205 0.2 + vertex 4.2282419252939 0.592555136977205 0.0 + endloop +endfacet +facet normal 0.992273219682165 0.124071985160193 0.0 + outer loop + vertex 4.2282419252939 0.592555136977205 0.2 + vertex 4.22568265805252 0.63734979693889 0.0 + vertex 4.22568265805252 0.63734979693889 0.2 + endloop +endfacet +facet normal 0.992273219682165 0.124071985160193 0.0 + outer loop + vertex 4.2282419252939 0.592555136977205 0.0 + vertex 4.23591972701803 0.552998743299044 0.2 + vertex 4.23591972701803 0.552998743299044 0.0 + endloop +endfacet +facet normal 0.962612437510528 0.27088243787673 0.0 + outer loop + vertex 4.23591972701803 0.552998743299044 0.2 + vertex 4.2282419252939 0.592555136977205 0.0 + vertex 4.2282419252939 0.592555136977205 0.2 + endloop +endfacet +facet normal 0.962612437510528 0.27088243787673 0.0 + outer loop + vertex 4.23591972701803 0.552998743299044 0.0 + vertex 4.24871606322493 0.518680615904408 0.2 + vertex 4.24871606322493 0.518680615904408 0.0 + endloop +endfacet +facet normal 0.898470012927288 0.439034891404362 0.0 + outer loop + vertex 4.24871606322493 0.518680615904408 0.2 + vertex 4.23591972701803 0.552998743299044 0.0 + vertex 4.23591972701803 0.552998743299044 0.2 + endloop +endfacet +facet normal 0.898470012927288 0.439034891404362 0.0 + outer loop + vertex 4.24871606322493 0.518680615904408 0.0 + vertex 4.26663093391458 0.489600754793297 0.2 + vertex 4.26663093391458 0.489600754793297 0.0 + endloop +endfacet +facet normal 0.789240434800171 0.614084306977826 0.0 + outer loop + vertex 4.26663093391458 0.489600754793297 0.2 + vertex 4.24871606322493 0.518680615904408 0.0 + vertex 4.24871606322493 0.518680615904408 0.2 + endloop +endfacet +facet normal 0.789240434800171 0.614084306977826 0.0 + outer loop + vertex 4.26663093391458 0.489600754793297 0.0 + vertex 4.28909561303336 0.466447617820117 0.2 + vertex 4.28909561303336 0.466447617820117 0.0 + endloop +endfacet +facet normal 0.628537365803979 0.777779390179629 0.0 + outer loop + vertex 4.28909561303336 0.466447617820117 0.2 + vertex 4.26663093391458 0.489600754793297 0.0 + vertex 4.26663093391458 0.489600754793297 0.2 + endloop +endfacet +facet normal 0.628537365803979 0.777779390179629 0.0 + outer loop + vertex 4.28909561303336 0.466447617820117 0.2 + vertex 4.31554137452761 0.449909662839274 0.0 + vertex 4.28909561303336 0.466447617820117 0.0 + endloop +endfacet +facet normal 0.423267384808038 0.906004812877815 0.0 + outer loop + vertex 4.31554137452761 0.449909662839274 0.0 + vertex 4.28909561303336 0.466447617820117 0.2 + vertex 4.31554137452761 0.449909662839274 0.2 + endloop +endfacet +facet normal 0.423267384808038 0.906004812877815 0.0 + outer loop + vertex 4.31554137452761 0.449909662839274 0.2 + vertex 4.34596821839734 0.439986889850768 0.0 + vertex 4.31554137452761 0.449909662839274 0.0 + endloop +endfacet +facet normal 0.204094833430253 0.978951121847806 0.0 + outer loop + vertex 4.34596821839734 0.439986889850768 0.0 + vertex 4.31554137452761 0.449909662839274 0.2 + vertex 4.34596821839734 0.439986889850768 0.2 + endloop +endfacet +facet normal 0.204094833430253 0.978951121847806 0.0 + outer loop + vertex 4.34596821839734 0.439986889850768 0.2 + vertex 4.38037614464255 0.436679298854599 0.0 + vertex 4.34596821839734 0.439986889850768 0.0 + endloop +endfacet +facet normal -0.00481129071356181 0.999988425673852 0.0 + outer loop + vertex 4.38037614464255 0.436679298854599 0.0 + vertex 4.34596821839734 0.439986889850768 0.2 + vertex 4.38037614464255 0.436679298854599 0.2 + endloop +endfacet +facet normal -0.00481129071356181 0.999988425673852 0.0 + outer loop + vertex 4.38037614464255 0.436679298854599 0.2 + vertex 4.42335986111765 0.441229107283718 0.0 + vertex 4.38037614464255 0.436679298854599 0.0 + endloop +endfacet +facet normal -0.212412834660519 0.977180018047587 0.0 + outer loop + vertex 4.42335986111765 0.441229107283718 0.0 + vertex 4.38037614464255 0.436679298854599 0.2 + vertex 4.42335986111765 0.441229107283718 0.2 + endloop +endfacet +facet normal -0.212412834660519 0.977180018047587 0.0 + outer loop + vertex 4.42335986111765 0.441229107283718 0.2 + vertex 4.46418840517895 0.454878532571074 0.0 + vertex 4.42335986111765 0.441229107283718 0.0 + endloop +endfacet +facet normal -0.40318983395436 0.915116362981154 0.0 + outer loop + vertex 4.46418840517895 0.454878532571074 0.0 + vertex 4.42335986111765 0.441229107283718 0.2 + vertex 4.46418840517895 0.454878532571074 0.2 + endloop +endfacet +facet normal -0.40318983395436 0.915116362981154 0.0 + outer loop + vertex 4.46418840517895 0.454878532571074 0.2 + vertex 4.5016644588188 0.475711865904408 0.0 + vertex 4.46418840517895 0.454878532571074 0.0 + endloop +endfacet +facet normal -0.555388575177809 0.831590963492247 0.0 + outer loop + vertex 4.5016644588188 0.475711865904408 0.0 + vertex 4.46418840517895 0.454878532571074 0.2 + vertex 4.5016644588188 0.475711865904408 0.2 + endloop +endfacet +facet normal -0.555388575177809 0.831590963492247 0.0 + outer loop + vertex 4.5016644588188 0.475711865904408 0.2 + vertex 4.53459070402953 0.501813398471458 0.0 + vertex 4.5016644588188 0.475711865904408 0.0 + endloop +endfacet +facet normal -0.621213734155876 0.783641178407639 0.0 + outer loop + vertex 4.53459070402953 0.501813398471458 0.0 + vertex 4.5016644588188 0.475711865904408 0.2 + vertex 4.53459070402953 0.501813398471458 0.2 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 4.53459070402953 0.501813398471458 0.2 + vertex 4.53985890326324 0.501813398471458 0.0 + vertex 4.53459070402953 0.501813398471458 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 4.53985890326324 0.501813398471458 0.0 + vertex 4.53459070402953 0.501813398471458 0.2 + vertex 4.53985890326324 0.501813398471458 0.2 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.53985890326324 0.501813398471458 0.0 + vertex 4.53985890326324 0.400759758624714 0.2 + vertex 4.53985890326324 0.400759758624714 0.0 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.53985890326324 0.400759758624714 0.2 + vertex 4.53985890326324 0.501813398471458 0.0 + vertex 4.53985890326324 0.501813398471458 0.2 + endloop +endfacet +facet normal 0.410364677328798 -0.911921505175107 0.0 + outer loop + vertex 4.53985890326324 0.400759758624714 0.2 + vertex 4.50633399904869 0.385673551728163 0.0 + vertex 4.53985890326324 0.400759758624714 0.0 + endloop +endfacet +facet normal 0.375577351678058 -0.926791051373769 0.0 + outer loop + vertex 4.50633399904869 0.385673551728163 0.0 + vertex 4.53985890326324 0.400759758624714 0.2 + vertex 4.50633399904869 0.385673551728163 0.2 + endloop +endfacet +facet normal 0.375577351678058 -0.926791051373769 0.0 + outer loop + vertex 4.50633399904869 0.385673551728163 0.2 + vertex 4.46993553161574 0.372503053643871 0.0 + vertex 4.50633399904869 0.385673551728163 0.0 + endloop +endfacet +facet normal 0.291774960866941 -0.956486995317289 0.0 + outer loop + vertex 4.46993553161574 0.372503053643871 0.0 + vertex 4.50633399904869 0.385673551728163 0.2 + vertex 4.46993553161574 0.372503053643871 0.2 + endloop +endfacet +facet normal 0.291774960866941 -0.956486995317289 0.0 + outer loop + vertex 4.46993553161574 0.372503053643871 0.2 + vertex 4.42874779215213 0.362206118777971 0.0 + vertex 4.46993553161574 0.372503053643871 0.0 + endloop +endfacet +facet normal 0.159989675112747 -0.98711868782701 0.0 + outer loop + vertex 4.42874779215213 0.362206118777971 0.0 + vertex 4.46993553161574 0.372503053643871 0.2 + vertex 4.42874779215213 0.362206118777971 0.2 + endloop +endfacet +facet normal 0.159989675112747 -0.98711868782701 0.0 + outer loop + vertex 4.42874779215213 0.362206118777971 0.2 + vertex 4.38181292625175 0.358614164754983 0.0 + vertex 4.42874779215213 0.362206118777971 0.0 + endloop +endfacet +facet normal -0.00197196685745208 -0.999998055671466 0.0 + outer loop + vertex 4.38181292625175 0.358614164754983 0.0 + vertex 4.42874779215213 0.362206118777971 0.2 + vertex 4.38181292625175 0.358614164754983 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.71370947797589 0.370587344831611 0.2 + vertex 4.62367116379964 0.905549030655366 0.2 + vertex 4.62367116379964 0.370587344831611 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.62367116379964 0.905549030655366 0.2 + vertex 4.71370947797589 0.370587344831611 0.2 + vertex 4.71370947797589 0.905549030655366 0.2 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.71370947797589 0.370587344831611 0.2 + vertex 4.62367116379964 0.370587344831611 0.0 + vertex 4.71370947797589 0.370587344831611 0.0 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.62367116379964 0.370587344831611 0.0 + vertex 4.71370947797589 0.370587344831611 0.2 + vertex 4.62367116379964 0.370587344831611 0.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 4.62367116379964 0.905549030655366 0.2 + vertex 4.62367116379964 0.370587344831611 0.0 + vertex 4.62367116379964 0.370587344831611 0.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 4.62367116379964 0.370587344831611 0.0 + vertex 4.62367116379964 0.905549030655366 0.2 + vertex 4.62367116379964 0.905549030655366 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 4.62367116379964 0.905549030655366 0.2 + vertex 4.71370947797589 0.905549030655366 0.0 + vertex 4.62367116379964 0.905549030655366 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 4.71370947797589 0.905549030655366 0.0 + vertex 4.62367116379964 0.905549030655366 0.2 + vertex 4.71370947797589 0.905549030655366 0.2 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.71370947797589 0.905549030655366 0.0 + vertex 4.71370947797589 0.370587344831611 0.2 + vertex 4.71370947797589 0.370587344831611 0.0 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.71370947797589 0.370587344831611 0.2 + vertex 4.71370947797589 0.905549030655366 0.0 + vertex 4.71370947797589 0.905549030655366 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.71993553161574 0.995108417628546 0.2 + vertex 4.6174451101598 1.08849922222625 0.2 + vertex 4.6174451101598 0.995108417628546 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.6174451101598 1.08849922222625 0.2 + vertex 4.71993553161574 0.995108417628546 0.2 + vertex 4.71993553161574 1.08849922222625 0.2 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.71993553161574 0.995108417628546 0.2 + vertex 4.6174451101598 0.995108417628546 0.0 + vertex 4.71993553161574 0.995108417628546 0.0 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.6174451101598 0.995108417628546 0.0 + vertex 4.71993553161574 0.995108417628546 0.2 + vertex 4.6174451101598 0.995108417628546 0.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 4.6174451101598 1.08849922222625 0.2 + vertex 4.6174451101598 0.995108417628546 0.0 + vertex 4.6174451101598 0.995108417628546 0.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 4.6174451101598 0.995108417628546 0.0 + vertex 4.6174451101598 1.08849922222625 0.2 + vertex 4.6174451101598 1.08849922222625 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 4.6174451101598 1.08849922222625 0.2 + vertex 4.71993553161574 1.08849922222625 0.0 + vertex 4.6174451101598 1.08849922222625 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 4.71993553161574 1.08849922222625 0.0 + vertex 4.6174451101598 1.08849922222625 0.2 + vertex 4.71993553161574 1.08849922222625 0.2 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.71993553161574 1.08849922222625 0.0 + vertex 4.71993553161574 0.995108417628546 0.2 + vertex 4.71993553161574 0.995108417628546 0.0 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.71993553161574 0.995108417628546 0.2 + vertex 4.71993553161574 1.08849922222625 0.0 + vertex 4.71993553161574 1.08849922222625 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.93784740901037 0.370587344831611 0.2 + vertex 4.84780909483413 0.905549030655366 0.2 + vertex 4.84780909483413 0.370587344831611 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.84780909483413 0.905549030655366 0.2 + vertex 4.93784740901037 0.370587344831611 0.2 + vertex 4.93784740901037 0.905549030655366 0.2 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.93784740901037 0.370587344831611 0.2 + vertex 4.84780909483413 0.370587344831611 0.0 + vertex 4.93784740901037 0.370587344831611 0.0 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.84780909483413 0.370587344831611 0.0 + vertex 4.93784740901037 0.370587344831611 0.2 + vertex 4.84780909483413 0.370587344831611 0.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 4.84780909483413 0.905549030655366 0.2 + vertex 4.84780909483413 0.370587344831611 0.0 + vertex 4.84780909483413 0.370587344831611 0.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 4.84780909483413 0.370587344831611 0.0 + vertex 4.84780909483413 0.905549030655366 0.2 + vertex 4.84780909483413 0.905549030655366 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 4.84780909483413 0.905549030655366 0.2 + vertex 4.93784740901037 0.905549030655366 0.0 + vertex 4.84780909483413 0.905549030655366 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 4.93784740901037 0.905549030655366 0.0 + vertex 4.84780909483413 0.905549030655366 0.2 + vertex 4.93784740901037 0.905549030655366 0.2 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.93784740901037 0.905549030655366 0.0 + vertex 4.93784740901037 0.370587344831611 0.2 + vertex 4.93784740901037 0.370587344831611 0.0 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.93784740901037 0.370587344831611 0.2 + vertex 4.93784740901037 0.905549030655366 0.0 + vertex 4.93784740901037 0.905549030655366 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.94407346265022 0.995108417628546 0.2 + vertex 4.84158304119428 1.08849922222625 0.2 + vertex 4.84158304119428 0.995108417628546 0.2 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 4.84158304119428 1.08849922222625 0.2 + vertex 4.94407346265022 0.995108417628546 0.2 + vertex 4.94407346265022 1.08849922222625 0.2 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.94407346265022 0.995108417628546 0.2 + vertex 4.84158304119428 0.995108417628546 0.0 + vertex 4.94407346265022 0.995108417628546 0.0 + endloop +endfacet +facet normal 0.0 -1.0 0.0 + outer loop + vertex 4.84158304119428 0.995108417628546 0.0 + vertex 4.94407346265022 0.995108417628546 0.2 + vertex 4.84158304119428 0.995108417628546 0.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 4.84158304119428 1.08849922222625 0.2 + vertex 4.84158304119428 0.995108417628546 0.0 + vertex 4.84158304119428 0.995108417628546 0.2 + endloop +endfacet +facet normal -1.0 0.0 0.0 + outer loop + vertex 4.84158304119428 0.995108417628546 0.0 + vertex 4.84158304119428 1.08849922222625 0.2 + vertex 4.84158304119428 1.08849922222625 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 4.84158304119428 1.08849922222625 0.2 + vertex 4.94407346265022 1.08849922222625 0.0 + vertex 4.84158304119428 1.08849922222625 0.0 + endloop +endfacet +facet normal 0.0 1.0 0.0 + outer loop + vertex 4.94407346265022 1.08849922222625 0.0 + vertex 4.84158304119428 1.08849922222625 0.2 + vertex 4.94407346265022 1.08849922222625 0.2 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.94407346265022 1.08849922222625 0.0 + vertex 4.94407346265022 0.995108417628546 0.2 + vertex 4.94407346265022 0.995108417628546 0.0 + endloop +endfacet +facet normal 1.0 0.0 0.0 + outer loop + vertex 4.94407346265022 0.995108417628546 0.2 + vertex 4.94407346265022 1.08849922222625 0.0 + vertex 4.94407346265022 1.08849922222625 0.2 + endloop +endfacet +endsolid Untitled-5427e5af diff --git a/test/testfiles/stl/stl_binary.stl b/test/testfiles/stl/stl_binary.stl new file mode 100644 index 0000000..dcb5ab8 Binary files /dev/null and b/test/testfiles/stl/stl_binary.stl differ diff --git a/test/testfiles/stl/two_triangles.stl b/test/testfiles/stl/two_triangles.stl new file mode 100644 index 0000000..9d65790 --- /dev/null +++ b/test/testfiles/stl/two_triangles.stl @@ -0,0 +1,16 @@ +solid MeshName +facet normal 0.0 0.0 1.0 + outer loop + vertex 0.0 0.0 0.0 + vertex 1.0 0.0 0.0 + vertex 1.0 1.0 0.0 + endloop +endfacet +facet normal 0.0 0.0 1.0 + outer loop + vertex 0.0 0.0 1.0 + vertex 1.0 0.0 1.0 + vertex 1.0 1.0 1.0 + endloop +endfacet +endsolid MeshName diff --git a/test/tests/exporter_test.js b/test/tests/exporter_test.js new file mode 100644 index 0000000..3a1b44d --- /dev/null +++ b/test/tests/exporter_test.js @@ -0,0 +1,364 @@ +var assert = require ('assert'); + +function CreateTestModel () +{ + let model = new OV.Model (); + + let material1 = new OV.Material (); + material1.name = 'TestMaterial1'; + material1.ambient = new OV.Color (0, 0, 0); + material1.diffuse = new OV.Color (255, 0, 0); + material1.specular = new OV.Color (51, 51, 51); + material1.diffuseMap = new OV.TextureMap (); + material1.diffuseMap.name = 'textures/texture1.png'; + material1.diffuseMap.url = 'texture1_url'; + material1.specularMap = new OV.TextureMap (); + material1.specularMap.name = 'textures/texture2.png'; + material1.specularMap.url = 'texture2_url'; + material1.bumpMap = new OV.TextureMap (); + material1.bumpMap.name = 'textures/texture3.png'; + material1.bumpMap.url = 'texture3_url'; + model.AddMaterial (material1); + + let material2 = new OV.Material (); + material2.name = 'TestMaterial2'; + material2.ambient = new OV.Color (0, 0, 0); + material2.diffuse = new OV.Color (0, 255, 0); + material2.specular = new OV.Color (51, 51, 51); + model.AddMaterial (material2); + + let mesh1 = new OV.Mesh (); + mesh1.SetName ('TestMesh1'); + mesh1.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh1.AddVertex (new OV.Coord3D (1.0, 0.0, 1.0)); + mesh1.AddVertex (new OV.Coord3D (0.0, 1.0, 1.0)); + let triangle1 = new OV.Triangle (0, 1, 2); + triangle1.mat = 0; + mesh1.AddTriangle (triangle1); + model.AddMesh (mesh1); + + let mesh2 = new OV.Mesh (); + mesh2.SetName ('TestMesh2'); + mesh2.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh2.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh2.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0)); + mesh2.AddVertex (new OV.Coord3D (-1.0, 0.0, 0.0)); + mesh2.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0)); + let triangle2 = new OV.Triangle (0, 1, 2); + let triangle3 = new OV.Triangle (0, 2, 3); + let triangle4 = new OV.Triangle (0, 4, 2); + triangle2.mat = 0; + triangle3.mat = 1; + triangle4.mat = 1; + mesh2.AddTriangle (triangle2); + mesh2.AddTriangle (triangle3); + mesh2.AddTriangle (triangle4); + model.AddMesh (mesh2); + + OV.FinalizeModel (model, null); + return model; +} + +describe ('Exporter', function () { + it ('Obj Export', function () { + let model = CreateTestModel (); + let exporter = new OV.Exporter (); + + let result = exporter.Export (model, OV.FileFormat.Text, 'obj', null); + assert.strictEqual (result.length, 5); + + let mtlFile = result[0]; + assert.strictEqual (mtlFile.GetName (), 'model.mtl'); + assert.strictEqual (mtlFile.GetContent (), +`newmtl TestMaterial1 +Ka 0 0 0 +Kd 1 0 0 +Ks 0.2 0.2 0.2 +Ns 0 +d 1 +map_Kd texture1.png +map_Ks texture2.png +bump texture3.png +newmtl TestMaterial2 +Ka 0 0 0 +Kd 0 1 0 +Ks 0.2 0.2 0.2 +Ns 0 +d 1 +` + ); + let objFile = result[1]; + assert.strictEqual (objFile.GetName (), 'model.obj'); + assert.strictEqual (objFile.GetContent (), +`mtllib model.mtl +g TestMesh1 +v 0 0 1 +v 1 0 1 +v 0 1 1 +vn 0 0 1 +usemtl TestMaterial1 +f 1//1 2//1 3//1 +g TestMesh2 +v 0 0 0 +v 1 0 0 +v 0 1 0 +v -1 0 0 +v 0 0 1 +vn 0 0 1 +vn 0 0 1 +vn -1 0 0 +f 4//2 5//2 6//2 +usemtl TestMaterial2 +f 4//3 6//3 7//3 +f 4//4 8//4 6//4 +` + ); + + let textureFile1 = result[2]; + assert.strictEqual (textureFile1.GetName (), 'texture1.png'); + assert.strictEqual (textureFile1.GetUrl (), 'texture1_url'); + assert.strictEqual (textureFile1.GetContent (), null); + + let textureFile2 = result[3]; + assert.strictEqual (textureFile2.GetName (), 'texture2.png'); + assert.strictEqual (textureFile2.GetUrl (), 'texture2_url'); + assert.strictEqual (textureFile2.GetContent (), null); + + let textureFile3 = result[4]; + assert.strictEqual (textureFile3.GetName (), 'texture3.png'); + assert.strictEqual (textureFile3.GetUrl (), 'texture3_url'); + assert.strictEqual (textureFile3.GetContent (), null); + }); + + it ('Stl Export', function () { + let model = CreateTestModel (); + let exporter = new OV.Exporter (); + + let result = exporter.Export (model, OV.FileFormat.Text, 'stl', null); + assert.strictEqual (result.length, 1); + + let stlFile = result[0]; + assert.strictEqual (stlFile.GetName (), 'model.stl'); + assert.strictEqual (stlFile.GetContent (), +`solid Model +facet normal 0 0 1 + outer loop + vertex 0 0 1 + vertex 1 0 1 + vertex 0 1 1 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 0 0 + vertex 1 0 0 + vertex 0 1 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 0 0 + vertex 0 1 0 + vertex -1 0 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 0 0 + vertex 0 0 1 + vertex 0 1 0 + endloop +endfacet +endsolid Model +` + ); + }); + + it ('Stl Binary Export', function () { + let model = CreateTestModel (); + let exporter = new OV.Exporter (); + + let result = exporter.Export (model, OV.FileFormat.Binary, 'stl', null); + assert.strictEqual (result.length, 1); + + let stlFile = result[0]; + assert.strictEqual (stlFile.GetName (), 'model.stl'); + assert.strictEqual (stlFile.GetContent ().byteLength, 284); + + let contentBuffer = stlFile.GetContent (); + let importer = new OV.ImporterStl (); + importer.Import (contentBuffer, 'stl', { + getDefaultMaterial () { + return new OV.Material (); + } + }); + let importedModel = importer.GetModel (); + assert.strictEqual (importedModel.VertexCount (), 12); + assert.strictEqual (importedModel.TriangleCount (), 4); + }); + + it ('Off Export', function () { + let model = CreateTestModel (); + let exporter = new OV.Exporter (); + + let result = exporter.Export (model, OV.FileFormat.Text, 'off', null); + assert.strictEqual (result.length, 1); + + let offFile = result[0]; + assert.strictEqual (offFile.GetName (), 'model.off'); + assert.strictEqual (offFile.GetContent (), +`OFF +8 4 0 +0 0 1 +1 0 1 +0 1 1 +0 0 0 +1 0 0 +0 1 0 +-1 0 0 +0 0 1 +3 0 1 2 +3 3 4 5 +3 3 5 6 +3 3 7 5 +` + ); + }); + + it ('Ply Export', function () { + let model = CreateTestModel (); + let exporter = new OV.Exporter (); + + let result = exporter.Export (model, OV.FileFormat.Text, 'ply', null); + assert.strictEqual (result.length, 1); + + let plyFile = result[0]; + assert.strictEqual (plyFile.GetName (), 'model.ply'); + assert.strictEqual (plyFile.GetContent (), +`ply +format ascii 1.0 +element vertex 8 +property float x +property float y +property float z +element face 4 +property list uchar int vertex_index +end_header +0 0 1 +1 0 1 +0 1 1 +0 0 0 +1 0 0 +0 1 0 +-1 0 0 +0 0 1 +3 0 1 2 +3 3 4 5 +3 3 5 6 +3 3 7 5 +` + ); + }); + + it ('Ply Binary Export', function () { + let model = CreateTestModel (); + let exporter = new OV.Exporter (); + + let result = exporter.Export (model, OV.FileFormat.Binary, 'ply', null); + assert.strictEqual (result.length, 1); + + let plyFile = result[0]; + assert.strictEqual (plyFile.GetName (), 'model.ply'); + assert.strictEqual (plyFile.GetContent ().byteLength, 315); + + let contentBuffer = plyFile.GetContent (); + let importer = new OV.ImporterPly (); + importer.Import (contentBuffer, 'ply', { + getDefaultMaterial () { + return new OV.Material (); + } + }); + let importedModel = importer.GetModel (); + assert.strictEqual (importedModel.VertexCount (), 8); + assert.strictEqual (importedModel.TriangleCount (), 4); + }); + + it ('Gltf Ascii Export', function () { + let model = CreateTestModel (); + let exporter = new OV.Exporter (); + + let result = exporter.Export (model, OV.FileFormat.Text, 'gltf', null); + assert.strictEqual (result.length, 3); + + let gltfFile = result[0]; + let binFile = result[1]; + let textureFile = result[2]; + assert.strictEqual (gltfFile.GetName (), 'model.gltf'); + assert.strictEqual (binFile.GetName (), 'model.bin'); + + assert.strictEqual (textureFile.GetName (), 'texture1.png'); + assert.strictEqual (textureFile.GetUrl (), 'texture1_url'); + assert.strictEqual (textureFile.GetContent (), null); + + let contentBuffer = gltfFile.GetContent (); + let importer = new OV.ImporterGltf (); + importer.Import (contentBuffer, 'gltf', { + getDefaultMaterial () { + return new OV.Material (); + }, + getFileContent (filePath) { + return binFile.GetContent (); + }, + getTextureObjectUrl (filePath) { + return null; + } + }); + + let importedModel = importer.GetModel (); + assert (OV.CheckModel (importedModel)); + assert.strictEqual (importedModel.MaterialCount (), 2); + assert.strictEqual (importedModel.MeshCount (), 2); + assert.strictEqual (importedModel.GetMesh (0).GetName (), 'TestMesh1'); + assert.strictEqual (importedModel.GetMesh (1).GetName (), 'TestMesh2'); + assert.strictEqual (importedModel.VertexCount (), 12); + assert.strictEqual (importedModel.TriangleCount (), 4); + }); + + it ('Gltf Binary Export', function () { + let model = CreateTestModel (); + let exporter = new OV.Exporter (); + + let result = exporter.Export (model, OV.FileFormat.Binary, 'glb', { + getTextureBuffer : function (name) { + return new ArrayBuffer (4); + } + }); + assert.strictEqual (result.length, 1); + + let glbFile = result[0]; + assert.strictEqual (glbFile.GetName (), 'model.glb'); + + let contentBuffer = glbFile.GetContent (); + let importer = new OV.ImporterGltf (); + importer.Import (contentBuffer, 'glb', { + getDefaultMaterial () { + return new OV.Material (); + }, + getFileContent (filePath) { + return binFile.GetContent (); + }, + getTextureObjectUrl (filePath) { + return null; + } + }); + + let importedModel = importer.GetModel (); + assert (OV.CheckModel (importedModel)); + assert.strictEqual (importedModel.MaterialCount (), 2); + assert.strictEqual (importedModel.MeshCount (), 2); + assert.strictEqual (importedModel.GetMesh (0).GetName (), 'TestMesh1'); + assert.strictEqual (importedModel.GetMesh (1).GetName (), 'TestMesh2'); + assert.strictEqual (importedModel.VertexCount (), 12); + assert.strictEqual (importedModel.TriangleCount (), 4); + }); +}); diff --git a/test/tests/fileutils_test.js b/test/tests/fileutils_test.js new file mode 100644 index 0000000..bc61a95 --- /dev/null +++ b/test/tests/fileutils_test.js @@ -0,0 +1,41 @@ +var assert = require ('assert'); + +function GetLines (str) +{ + var lines = []; + OV.ReadLines (str, function (line) { + lines.push (line); + }); + return lines; +} + +describe ('File Utils', function () { + it ('Get File Extension', function () { + assert.strictEqual (OV.GetFileExtension ('file'), ''); + assert.strictEqual (OV.GetFileExtension ('file.obj'), 'obj'); + assert.strictEqual (OV.GetFileExtension ('file.OBJ'), 'OBJ'); + }); + + it ('Get File Name', function () { + assert.strictEqual (OV.GetFileName ('file'), 'file'); + assert.strictEqual (OV.GetFileName ('file.obj'), 'file.obj'); + assert.strictEqual (OV.GetFileName ('file.OBJ'), 'file.OBJ'); + assert.strictEqual (OV.GetFileName ('folder/file'), 'file'); + assert.strictEqual (OV.GetFileName ('folder/file.obj'), 'file.obj'); + assert.strictEqual (OV.GetFileName ('folder/file.OBJ'), 'file.OBJ'); + assert.strictEqual (OV.GetFileName ('folder\\file'), 'file'); + assert.strictEqual (OV.GetFileName ('folder\\file.obj'), 'file.obj'); + assert.strictEqual (OV.GetFileName ('folder\\file.OBJ'), 'file.OBJ'); + }); + + it ('Read Lines', function () { + assert.deepStrictEqual (GetLines (''), []); + assert.deepStrictEqual (GetLines ('\n'), []); + assert.deepStrictEqual (GetLines ('\r\n'), []); + assert.deepStrictEqual (GetLines ('a\nb'), ['a', 'b']); + assert.deepStrictEqual (GetLines ('apple\nbanana'), ['apple', 'banana']); + assert.deepStrictEqual (GetLines ('apple\r\nbanana'), ['apple', 'banana']); + assert.deepStrictEqual (GetLines ('apple\r\n'), ['apple']); + assert.deepStrictEqual (GetLines ('\r\napple\r\n'), ['apple']); + }); +}); diff --git a/test/tests/geometry_test.js b/test/tests/geometry_test.js new file mode 100644 index 0000000..ba28433 --- /dev/null +++ b/test/tests/geometry_test.js @@ -0,0 +1,146 @@ +var assert = require ('assert'); + +function CreateYRot90Quaternion () +{ + let angle = Math.PI / 2.0; + let rotX = 0.0; + let rotY = 1.0; + let rotZ = 0.0; + let quaternion = [ + Math.sin (angle / 2.0) * rotX, + Math.sin (angle / 2.0) * rotY, + Math.sin (angle / 2.0) * rotZ, + Math.cos (angle / 2.0) + ]; + return quaternion; +} + +describe ('Coord', function () { + it ('Length', function () { + var c = new OV.Coord3D (2.0, 0.0, 0.0); + assert.strictEqual (c.Length (), 2.0); + }); + + it ('Multiply Scalar', function () { + var c = new OV.Coord3D (2.0, 0.0, 0.0); + c.MultiplyScalar (3.0); + assert.strictEqual (c.x, 6.0); + assert.strictEqual (c.y, 0.0); + assert.strictEqual (c.z, 0.0); + }); + + it ('Normalize', function () { + var c = new OV.Coord3D (2.0, 0.0, 0.0); + c.Normalize (); + assert.strictEqual (c.x, 1.0); + assert.strictEqual (c.y, 0.0); + assert.strictEqual (c.z, 0.0); + }); +}); + +describe ('Triangle', function() { + it ('Calculate Normal', function () { + var normal = OV.CalculateTriangleNormal ( + new OV.Coord3D (0.0, 0.0, 0.0), + new OV.Coord3D (1.0, 0.0, 0.0), + new OV.Coord3D (1.0, 1.0, 0.0) + ); + assert.strictEqual (normal.x, 0.0); + assert.strictEqual (normal.y, 0.0); + assert.strictEqual (normal.z, 1.0); + }); +}); + +describe ('Transformation', function() { + it ('Basic Test', function () { + let rotation = CreateYRot90Quaternion (); + + let coord = new OV.Coord3D (1.0, 2.0, 3.0); + + let tr = new OV.Transformation (); + assert (tr.IsIdentity ()); + assert (OV.CoordIsEqual3D (tr.TransformCoord3D (coord), new OV.Coord3D (1.0, 2.0, 3.0))); + + tr.AppendMatrix (new OV.Matrix ().CreateScale (3.0, 4.0, 5.0)); + assert (!tr.IsIdentity ()); + assert (OV.CoordIsEqual3D (tr.TransformCoord3D (coord), new OV.Coord3D (3.0, 8.0, 15.0))); + + tr.AppendMatrix (new OV.Matrix ().CreateRotation (rotation[0], rotation[1], rotation[2], rotation[3])); + assert (!tr.IsIdentity ()); + assert (OV.CoordIsEqual3D (tr.TransformCoord3D (coord), new OV.Coord3D (15.0, 8.0, -3.0))); + + tr.AppendMatrix (new OV.Matrix ().CreateTranslation (4.0, 5.0, 6.0)); + assert (!tr.IsIdentity ()); + assert (OV.CoordIsEqual3D (tr.TransformCoord3D (coord), new OV.Coord3D (19.0, 13.0, 3.0))); + + let tr2 = new OV.Transformation (); + tr2.SetMatrix (new OV.Matrix ().ComposeTRS ([4.0, 5.0, 6.0], rotation, [3.0, 4.0, 5.0])); + assert (OV.CoordIsEqual3D (tr2.TransformCoord3D (coord), new OV.Coord3D (19.0, 13.0, 3.0))); + }); + + it ('TRS Compose Test', function () { + let rotation = CreateYRot90Quaternion (); + let coord = new OV.Coord3D (1.0, 2.0, 3.0); + + let tr = new OV.Transformation (); + tr.SetMatrix (new OV.Matrix ().ComposeTRS ([4.0, 5.0, 6.0], rotation, [3.0, 4.0, 5.0])); + assert (OV.CoordIsEqual3D (tr.TransformCoord3D (coord), new OV.Coord3D (19.0, 13.0, 3.0))); + }); + + it ('TRS Compose Test', function () { + let rotation = CreateYRot90Quaternion (); + let coord = new OV.Coord3D (1.0, 2.0, 3.0); + + let tr = new OV.Transformation (); + tr.SetMatrix (new OV.Matrix ().ComposeTRS ([4.0, 5.0, 6.0], rotation, [3.0, 4.0, 5.0])); + assert (OV.CoordIsEqual3D (tr.TransformCoord3D (coord), new OV.Coord3D (19.0, 13.0, 3.0))); + + let trs = tr.GetMatrix ().DecomposeTRS (); + let tr2 = new OV.Transformation (); + tr2.SetMatrix (new OV.Matrix ().ComposeTRS (trs.translation, trs.rotation, trs.scale)); + assert (OV.CoordIsEqual3D (tr2.TransformCoord3D (coord), new OV.Coord3D (19.0, 13.0, 3.0))); + }); + + it ('Default Quaternion Test', function () { + let coord = new OV.Coord3D (1.0, 2.0, 3.0); + + let tr = new OV.Transformation (); + tr.SetMatrix (new OV.Matrix ().CreateRotation (0.0, 0.0, 0.0, 1.0)); + assert (OV.CoordIsEqual3D (tr.TransformCoord3D (coord), coord)); + }); +}); + +describe ('Tween', function() { + it ('Linear Tween Function', function () { + assert (OV.IsEqual (OV.LinearTweenFunction (10.0, 0, 10), 0.0)); + assert (OV.IsEqual (OV.LinearTweenFunction (10.0, 1, 10), 1.0)); + assert (OV.IsEqual (OV.LinearTweenFunction (10.0, 5, 10), 5.0)); + assert (OV.IsEqual (OV.LinearTweenFunction (10.0, 9, 10), 9.0)); + assert (OV.IsEqual (OV.LinearTweenFunction (10.0, 10, 10), 10.0)); + }); + + it ('Bezier Tween Function', function () { + assert (OV.IsEqual (OV.BezierTweenFunction (10.0, 0, 10), 0.0)); + assert (OV.IsEqual (OV.BezierTweenFunction (10.0, 1, 10), 0.28)); + assert (OV.IsEqual (OV.BezierTweenFunction (10.0, 5, 10), 5.0)); + assert (OV.IsEqual (OV.BezierTweenFunction (10.0, 9, 10), 9.72)); + assert (OV.IsEqual (OV.BezierTweenFunction (10.0, 10, 10), 10.0)); + }); + + it ('Parabolic Tween Function', function () { + assert (OV.IsEqual (OV.ParabolicTweenFunction (10.0, 0, 10), 0.0)); + assert (OV.IsEqual (OV.ParabolicTweenFunction (10.0, 5, 10), 5.0)); + assert (OV.IsEqual (OV.ParabolicTweenFunction (10.0, 10, 10), 10.0)); + }); + + it ('Linear Tween Coordinates', function () { + let beg = new OV.Coord3D (0.0, 0.0, 0.0); + let end = new OV.Coord3D (9.0, 0.0, 0.0); + let segments = OV.TweenCoord3D (beg, end, 10, OV.LinearTweenFunction); + assert.strictEqual (segments.length, 10); + assert (OV.CoordIsEqual3D (segments[0], beg)); + assert (OV.CoordIsEqual3D (segments[1], new OV.Coord3D (1.0, 0.0, 0.0))); + assert (OV.CoordIsEqual3D (segments[5], new OV.Coord3D (5.0, 0.0, 0.0))); + assert (OV.CoordIsEqual3D (segments[segments.length - 1], end)); + }); +}); diff --git a/test/tests/importer3ds_test.js b/test/tests/importer3ds_test.js new file mode 100644 index 0000000..68b1314 --- /dev/null +++ b/test/tests/importer3ds_test.js @@ -0,0 +1,161 @@ +var assert = require ('assert'); +var testFiles = require ('../utils/testfiles.js'); +var testUtils = require ('../utils/testutils.js'); + +describe ('3ds Importer', function() { + it ('cube_with_materials.obj', function () { + var model = testFiles.Import3dsFile ('cube_with_materials.3ds'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'tex' }, + { name : 'red' }, + { name : 'green' }, + { name : 'blue' }, + { name : 'white' } + ], + meshes : [ + { + name : 'cube', + vertexCount : 8, + normalCount : 12, + uvCount : 8, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); + + it ('cube_with_texture_transformations.3ds', function () { + var model = testFiles.Import3dsFile ('cube_with_texture_transformations.3ds'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'tex' }, + { name : 'red' }, + { name : 'green' }, + { name : 'blue' }, + { name : 'white' } + ], + meshes : [ + { + name : 'cube', + vertexCount : 8, + normalCount : 12, + uvCount : 8, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); + + it ('cube_two_instances.3ds', function () { + var model = testFiles.Import3dsFile ('cube_two_instances.3ds'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'tex' }, + { name : 'red' }, + { name : 'green' }, + { name : 'blue' }, + { name : 'white' } + ], + meshes : [ + { + name : 'cube', + vertexCount : 8, + normalCount : 12, + uvCount : 8, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + }, + { + name : 'cube', + vertexCount : 8, + normalCount : 12, + uvCount : 8, + triangleCount : 12, + boundingBox : { + min : [2, 0, 0], + max : [3, 1, 1] + } + } + ] + }); + }); + + it ('cube_four_instances.3ds', function () { + var model = testFiles.Import3dsFile ('cube_four_instances.3ds'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'tex' }, + { name : 'red' }, + { name : 'green' }, + { name : 'blue' }, + { name : 'white' } + ], + meshes : [ + { + name : 'cube', + vertexCount : 8, + normalCount : 12, + uvCount : 8, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + }, + { + name : 'cube', + vertexCount : 8, + normalCount : 12, + uvCount : 8, + triangleCount : 12, + boundingBox : { + min : [2, 0, 0], + max : [3, 1, 1] + } + }, + { + name : 'cube', + vertexCount : 8, + normalCount : 12, + uvCount : 8, + triangleCount : 12, + boundingBox : { + min : [2, 2, 0], + max : [3, 3, 1] + } + }, + { + name : 'cube', + vertexCount : 8, + normalCount : 12, + uvCount : 8, + triangleCount : 12, + boundingBox : { + min : [0, 2, 0], + max : [1, 3, 1] + } + } + ] + }); + }); +}); diff --git a/test/tests/importergltf_test.js b/test/tests/importergltf_test.js new file mode 100644 index 0000000..da4fbc2 --- /dev/null +++ b/test/tests/importergltf_test.js @@ -0,0 +1,444 @@ +let assert = require ('assert'); +let testFiles = require ('../utils/testfiles.js'); +let testUtils = require ('../utils/testutils.js'); + +//const util = require('util') +//console.log (util.inspect(testUtils.ModelToObjectSimple (model), {showHidden: false, depth: null})) + +describe ('Gltf Importer', function () { + it ('Triangle', function () { + let testFileList = [ + ['Triangle/glTF', 'Triangle.gltf'], + ['Triangle/glTF-Embedded', 'Triangle.gltf'], + ['TriangleWithoutIndices/glTF', 'TriangleWithoutIndices.gltf'], + ['TriangleWithoutIndices/glTF-Embedded', 'TriangleWithoutIndices.gltf'] + ]; + for (let i = 0; i < testFileList.length; i++) { + let testFile = testFileList[i]; + let model = testFiles.ImportGltfFile (testFile[0], testFile[1]); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + vertexCount : 3, + normalCount : 1, + uvCount : 0, + triangleCount : 1, + boundingBox : { + min : [0.0, 0.0, 0.0], + max : [1.0, 1.0, 0.0] + } + } + ] + }); + } + }); + + it ('Box', function () { + let testFileList = [ + ['Box/glTF', 'Box.gltf'], + ['Box/glTF-Embedded', 'Box.gltf'], + ['Box/glTF-Binary', 'Box.glb'] + ]; + for (let i = 0; i < testFileList.length; i++) { + let testFile = testFileList[i]; + let model = testFiles.ImportGltfFile (testFile[0], testFile[1]); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'Red' } + ], + meshes : [ + { + name : 'Mesh', + vertexCount : 24, + normalCount : 24, + uvCount : 0, + triangleCount : 12, + boundingBox : { + min : [-0.5, -0.5, -0.5], + max : [0.5, 0.5, 0.5] + } + } + ] + }); + } + }); + + it ('BoxInterleaved', function () { + let testFileList = [ + ['BoxInterleaved/glTF', 'BoxInterleaved.gltf'], + ['BoxInterleaved/glTF-Embedded', 'BoxInterleaved.gltf'], + ['BoxInterleaved/glTF-Binary', 'BoxInterleaved.glb'] + ]; + for (let i = 0; i < testFileList.length; i++) { + let testFile = testFileList[i]; + let model = testFiles.ImportGltfFile (testFile[0], testFile[1]); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'Mesh', + vertexCount : 24, + normalCount : 24, + uvCount : 0, + triangleCount : 12, + boundingBox : { + min : [-0.5, -0.5, -0.5], + max : [0.5, 0.5, 0.5] + } + } + ] + }); + } + }); + + it ('BoxTextured', function () { + let testFileList = [ + ['BoxTextured/glTF', 'BoxTextured.gltf'], + ['BoxTextured/glTF-Embedded', 'BoxTextured.gltf'], + ['BoxTextured/glTF-Binary', 'BoxTextured.glb'] + ]; + for (let i = 0; i < testFileList.length; i++) { + let testFile = testFileList[i]; + let model = testFiles.ImportGltfFile (testFile[0], testFile[1]); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'Texture' } + ], + meshes : [ + { + name : 'Mesh', + vertexCount : 24, + normalCount : 24, + uvCount : 24, + triangleCount : 12, + boundingBox : { + min : [-0.5, -0.5, -0.5], + max : [0.5, 0.5, 0.5] + } + } + ] + }); + } + }); + + it ('SimpleMeshes', function () { + let testFileList = [ + ['SimpleMeshes/glTF', 'SimpleMeshes.gltf'], + ['SimpleMeshes/glTF-Embedded', 'SimpleMeshes.gltf'] + ]; + for (let i = 0; i < testFileList.length; i++) { + let testFile = testFileList[i]; + let model = testFiles.ImportGltfFile (testFile[0], testFile[1]); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + vertexCount : 3, + normalCount : 3, + uvCount : 0, + triangleCount : 1, + boundingBox : { + min : [0.0, 0.0, 0.0], + max : [1.0, 1.0, 0.0] + } + }, + { + name : '', + vertexCount : 3, + normalCount : 3, + uvCount : 0, + triangleCount : 1, + boundingBox : { + min : [1.0, 0.0, 0.0], + max : [2.0, 1.0, 0.0] + } + } + ] + }); + } + }); + + it ('OrientationTest', function () { + let testFileList = [ + ['OrientationTest/glTF', 'OrientationTest.gltf'], + ['OrientationTest/glTF-Embedded', 'OrientationTest.gltf'], + ['OrientationTest/glTF-Binary', 'OrientationTest.glb'], + ]; + for (let i = 0; i < testFileList.length; i++) { + let testFile = testFileList[i]; + let model = testFiles.ImportGltfFile (testFile[0], testFile[1]); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'BaseMaterial' }, + { name : 'MatX1' }, + { name : 'MatX2' }, + { name : 'MatY1' }, + { name : 'MatY2' }, + { name : 'MatZ1' }, + { name : 'MatZ2' } + ], + meshes : [ + { + name: 'ArrowMeshZ2', + vertexCount: 78, + normalCount: 78, + uvCount: 0, + triangleCount: 38, + boundingBox : { + min: [ -0.6921195564310951, -1.0785199551363698, -5.330651201963446 ], + max: [ 1.0439303242310798, 2.868914373727357, -4.66934876823423 ] + } + }, + { + name: 'TargetMeshZ2', + vertexCount: 50, + normalCount: 50, + uvCount: 0, + triangleCount: 26, + boundingBox : { + min: [ 0.8097413778305054, 2.8717148303985596, -5.33065128326416 ], + max: [ 1.4936277866363525, 3.9211390018463135, -4.66934871673584 ] + } + }, + { + name: 'TargetMeshY2', + vertexCount: 50, + normalCount: 50, + uvCount: 0, + triangleCount: 26, + boundingBox : { + min: [ -1.1686336994171143, -5.330650806427002, 2.93727445602417 ], + max: [ -0.46912679076194763, -4.66934871673584, 3.9916374683380127 ] + } + }, + { + name: 'ArrowMeshY2', + vertexCount: 78, + normalCount: 78, + uvCount: 0, + triangleCount: 38, + boundingBox : { + min: [ -0.9557393651589479, -5.330651177643153, -1.06505742884149 ], + max: [ 0.6167901044859734, -4.6693486435429055, 2.934442924096579 ] + } + }, + { + name: 'ArrowMeshX2', + vertexCount: 78, + normalCount: 78, + uvCount: 0, + triangleCount: 38, + boundingBox : { + min: [ -5.330651171390089, -1.0326269494863676, -0.6059335185163844 ], + max: [ -4.669348828609911, 2.9885841671721116, 0.8202131194767724 ] + } + }, + { + name: 'TargetMeshX2', + vertexCount: 54, + normalCount: 54, + uvCount: 0, + triangleCount: 26, + boundingBox : { + min: [ -5.33065128326416, 2.991360902786255, -0.012430161237716675 ], + max: [ -4.66934871673584, 4.039160251617432, 0.6999828815460205 ] + } + }, + { + name: 'TargetMeshZ1', + vertexCount: 52, + normalCount: 52, + uvCount: 0, + triangleCount: 26, + boundingBox : { + min: [ -1.3648574352264404, 2.9005930423736572, 4.66934871673584 ], + max: [ -0.6740907430648804, 3.9529545307159424, 5.33065128326416 ] + } + }, + { + name: 'ArrowMeshZ1', + vertexCount: 74, + normalCount: 74, + uvCount: 0, + triangleCount: 38, + boundingBox : { + min: [ -1.009571011381568, -1.074115497823536, 4.669348895549774 ], + max: [ 0.6625885973069405, 2.8977772707193465, 5.330651104450226 ] + } + }, + { + name: 'TargetMeshX1', + vertexCount: 54, + normalCount: 54, + uvCount: 0, + triangleCount: 26, + boundingBox : { + min: [ 4.66934871673584, 2.4595587253570557, -2.553251266479492 ], + max: [ 5.33065128326416, 3.432579517364502, -1.7226401567459106 ] + } + }, + { + name: 'ArrowMeshX1', + vertexCount: 78, + normalCount: 78, + uvCount: 0, + triangleCount: 38, + boundingBox : { + min: [ 4.669348835945129, -1.0589144181932033, -1.7207290818192755 ], + max: [ 5.330651164054871, 2.4574561383341145, 0.9159925616542917 ] + } + }, + { + name: 'TargetMeshY1', + vertexCount: 52, + normalCount: 52, + uvCount: 0, + triangleCount: 26, + boundingBox : { + min: [ 2.8218495845794678, 4.669349193572998, -1.6833229064941406 ], + max: [ 3.864471435546875, 5.33065128326416, -1.0113166570663452 ] + } + }, + { + name: 'ArrowMeshY1', + vertexCount: 78, + normalCount: 78, + uvCount: 0, + triangleCount: 38, + boundingBox : { + min: [ -1.0826615032554154, 4.669348806142807, -1.093071740019906 ], + max: [ 2.8190779754834807, 5.3306513130664825, 0.7348238365226794 ] + } + }, + { + name: 'BaseCubeMesh', + vertexCount: 272, + normalCount: 272, + uvCount: 0, + triangleCount: 140, + boundingBox : { + min: [ -5.000001907348633, -5, -5.000001907348633 ], + max: [ 5.000002384185791, 5, 5.000002861022949 ] + } + } + ] + }); + } + }); + + it ('MeshPrimitives_4Vertex', function () { + let testFileList = [ + ['Mesh_PrimitiveMode', 'Mesh_PrimitiveMode_04.gltf'], + ['Mesh_PrimitiveMode', 'Mesh_PrimitiveMode_05.gltf'], + ['Mesh_PrimitiveMode', 'Mesh_PrimitiveMode_11.gltf'], + ['Mesh_PrimitiveMode', 'Mesh_PrimitiveMode_12.gltf'], + ['Mesh_PrimitiveMode', 'Mesh_PrimitiveMode_13.gltf'] + ]; + for (let i = 0; i < testFileList.length; i++) { + let testFile = testFileList[i]; + let model = testFiles.ImportGltfFile (testFile[0], testFile[1]); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + vertexCount : 4, + normalCount : 2, + uvCount : 0, + triangleCount : 2, + boundingBox : { + min : [-0.5, -0.5, 0.0], + max : [0.5, 0.5, 0.0] + } + } + ] + }); + } + }); + + it ('MeshPrimitives_6Vertex', function () { + let testFileList = [ + ['Mesh_PrimitiveMode', 'Mesh_PrimitiveMode_06.gltf'] + ]; + for (let i = 0; i < testFileList.length; i++) { + let testFile = testFileList[i]; + let model = testFiles.ImportGltfFile (testFile[0], testFile[1]); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + vertexCount : 6, + normalCount : 2, + uvCount : 0, + triangleCount : 2, + boundingBox : { + min : [-0.5, -0.5, 0.0], + max : [0.5, 0.5, 0.0] + } + } + ] + }); + } + }); + + it ('SimpleSparseAccessor', function () { + let testFileList = [ + ['SimpleSparseAccessor/glTF', 'SimpleSparseAccessor.gltf'], + ['SimpleSparseAccessor/glTF-Embedded', 'SimpleSparseAccessor.gltf'] + ]; + for (let i = 0; i < testFileList.length; i++) { + let testFile = testFileList[i]; + let model = testFiles.ImportGltfFile (testFile[0], testFile[1]); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + vertexCount : 14, + normalCount : 12, + uvCount : 0, + triangleCount : 12, + boundingBox : { + min : [0.0, 0.0, 0.0], + max : [6.0, 4.0, 0.0] + } + } + ] + }); + } + }); +}); diff --git a/test/tests/importerobj_test.js b/test/tests/importerobj_test.js new file mode 100644 index 0000000..03cbce4 --- /dev/null +++ b/test/tests/importerobj_test.js @@ -0,0 +1,518 @@ +var assert = require ('assert'); +var testFiles = require ('../utils/testfiles.js'); +var testUtils = require ('../utils/testutils.js'); + +describe ('Obj Importer', function() { + it ('single_triangle.obj', function () { + var model = testFiles.ImportObjFile ('single_triangle.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('two_triangles.obj', function () { + var model = testFiles.ImportObjFile ('two_triangles.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + }, + { + vertices : [0, 0, 1, 1, 0, 1, 1, 1, 1], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_rectangle.obj', function () { + var model = testFiles.ImportObjFile ('single_rectangle.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + }, + { + vertices : [0, 0, 0, 1, 1, 0, 0, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_triangle_with_comments.obj', function () { + var model = testFiles.ImportObjFile ('single_triangle_with_comments.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'MyMeshName', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_triangle_negative_indices.obj', function () { + var model = testFiles.ImportObjFile ('single_triangle_negative_indices.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_triangle_with_normals_and_uvs.obj', function () { + var model = testFiles.ImportObjFile ('single_triangle_with_normals_and_uvs.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [0, 0, 1, 0, 1, 1], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_triangle_in_mesh.obj', function () { + var model = testFiles.ImportObjFile ('single_triangle_in_mesh.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'MeshName', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('two_triangles_in_meshes.obj', function () { + var model = testFiles.ImportObjFile ('two_triangles_in_meshes.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'MeshName1', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + }, + { + name : 'MeshName2', + triangles : [ + { + vertices : [0, 0, 1, 1, 0, 1, 1, 1, 1], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('empty_meshes.obj', function () { + var model = testFiles.ImportObjFile ('empty_meshes.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'MeshName1', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + }, + { + name : 'MeshName2', + triangles : [ + { + vertices : [0, 0, 1, 1, 0, 1, 1, 1, 1], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_triangle_with_material.obj', function () { + var model = testFiles.ImportObjFile ('single_triangle_with_material.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : 'MaterialName1' }, + { name : 'MaterialName2' } + ], + meshes : [ + { + name : 'MeshName', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('two_triangles_with_material.obj', function () { + var model = testFiles.ImportObjFile ('two_triangles_with_material.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : 'MaterialName1' }, + { name : 'MaterialName2' } + ], + meshes : [ + { + name : 'MeshName', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + }, + { + vertices : [0, 0, 1, 1, 0, 1, 1, 1, 1], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 1 + } + ] + } + ] + }); + }); + + it ('two_meshes_same_vertices.obj', function () { + var model = testFiles.ImportObjFile ('two_meshes_same_vertices.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'MeshName1', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + }, + { + name : 'MeshName2', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 0, 0, 1], + normals : [0, -1, 0, 0, -1, 0, 0, -1, 0], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('two_meshes_with_same_name.obj', function () { + var model = testFiles.ImportObjFile ('two_meshes_with_same_name.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'MeshName', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + }, + { + vertices : [0, 0, 0, 1, 0, 0, 0, 0, 1], + normals : [0, -1, 0, 0, -1, 0, 0, -1, 0], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('cube_with_materials.obj', function () { + var model = testFiles.ImportObjFile ('cube_with_materials.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'Red' }, + { name : 'Green' }, + { name : 'Blue' }, + { name : 'White' }, + { name : 'Texture' } + ], + meshes : [ + { + name : 'Cube', + vertexCount : 8, + normalCount : 6, + uvCount : 4, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); + + it ('cube_rgb.obj', function () { + var model = testFiles.ImportObjFile ('cube_rgb.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'Red' }, + { name : 'Green' }, + { name : 'Blue' }, + { name : 'White' } + ], + meshes : [ + { + name : 'Cube', + vertexCount : 8, + normalCount : 6, + uvCount : 4, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); + + it ('cube_with_materials_comments.obj', function () { + var model = testFiles.ImportObjFile ('cube_with_materials_comments.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'Red' }, + { name : 'Green' }, + { name : 'Blue' }, + { name : 'White' }, + { name : 'Texture' } + ], + meshes : [ + { + name : 'Cube', + vertexCount : 8, + normalCount : 6, + uvCount : 4, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); + + it ('cube with white spaces.obj', function () { + var model = testFiles.ImportObjFile ('cube with white spaces.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'Red Color' }, + { name : 'Green Color' }, + { name : 'Blue Color' }, + { name : 'White Color' }, + { name : 'Texture Color' } + ], + meshes : [ + { + name : 'Cube Mesh', + vertexCount : 8, + normalCount : 6, + uvCount : 4, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); + + it ('icosahedron.obj', function () { + var model = testFiles.ImportObjFile ('icosahedron.obj'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : 'Blue' } + ], + meshes : [ + { + name : 'Icosahedron', + vertexCount : 12, + normalCount : 20, + uvCount : 0, + triangleCount : 20, + boundingBox : { + min : [-0.85065080835204, -0.85065080835204, -0.85065080835204], + max : [0.85065080835204, 0.85065080835204, 0.85065080835204] + } + } + ] + }); + }); + + it ('hundred_cubes.obj', function () { + var model = testFiles.ImportObjFile ('hundred_cubes.obj'); + assert (OV.CheckModel (model)); + assert.strictEqual (model.MaterialCount (), 3); + assert.strictEqual (model.MeshCount (), 100); + }); +}); diff --git a/test/tests/importeroff_test.js b/test/tests/importeroff_test.js new file mode 100644 index 0000000..69cb86e --- /dev/null +++ b/test/tests/importeroff_test.js @@ -0,0 +1,137 @@ +var assert = require ('assert'); +var testFiles = require ('../utils/testfiles.js'); +var testUtils = require ('../utils/testutils.js'); + +describe ('Off Importer', function() { + it ('single_triangle.off', function () { + var model = testFiles.ImportOffFile ('single_triangle.off'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('two_triangles.off', function () { + var model = testFiles.ImportOffFile ('two_triangles.off'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + }, + { + vertices : [0, 0, 1, 1, 0, 1, 1, 1, 1], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_rectangle.off', function () { + var model = testFiles.ImportOffFile ('single_rectangle.off'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + }, + { + vertices : [0, 0, 0, 1, 1, 0, 0, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_triangle_with_comments.off', function () { + var model = testFiles.ImportOffFile ('single_triangle_with_comments.off'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('cube.off', function () { + var model = testFiles.ImportOffFile ('cube.off'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + vertexCount : 8, + normalCount : 12, + uvCount : 0, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); +}); diff --git a/test/tests/importerply_test.js b/test/tests/importerply_test.js new file mode 100644 index 0000000..f16e4ca --- /dev/null +++ b/test/tests/importerply_test.js @@ -0,0 +1,212 @@ +var assert = require ('assert'); +var testFiles = require ('../utils/testfiles.js'); +var testUtils = require ('../utils/testutils.js'); + +describe ('Ply Importer', function() { + it ('single_triangle.ply', function () { + var model = testFiles.ImportPlyFile ('single_triangle.ply'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('two_triangles.ply', function () { + var model = testFiles.ImportPlyFile ('two_triangles.ply'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + }, + { + vertices : [0, 0, 1, 1, 0, 1, 1, 1, 1], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_rectangle.ply', function () { + var model = testFiles.ImportPlyFile ('single_rectangle.ply'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + }, + { + vertices : [0, 0, 0, 1, 1, 0, 0, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_triangle_with_comments.ply', function () { + var model = testFiles.ImportPlyFile ('single_triangle_with_comments.ply'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('cube.ply', function () { + var model = testFiles.ImportPlyFile ('cube.ply'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + vertexCount : 8, + normalCount : 12, + uvCount : 0, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); + + it ('cube_meshlab_ascii.ply', function () { + var model = testFiles.ImportPlyFile ('cube_meshlab_ascii.ply'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + vertexCount : 8, + normalCount : 12, + uvCount : 0, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); + + it ('cube_meshlab_binary.ply', function () { + var model = testFiles.ImportPlyFile ('cube_meshlab_binary.ply'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + vertexCount : 8, + normalCount : 12, + uvCount : 0, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); + + it ('cube_rgb_binary.ply', function () { + var model = testFiles.ImportPlyFile ('cube_rgb_binary.ply'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '#cc0000ff' }, + { name : '#00cc00ff' }, + { name : '#0000ccff' }, + { name : '#cccccc7f' } + ], + meshes : [ + { + name : '', + vertexCount : 8, + normalCount : 12, + uvCount : 0, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); +}); diff --git a/test/tests/importerstl_test.js b/test/tests/importerstl_test.js new file mode 100644 index 0000000..1416b1f --- /dev/null +++ b/test/tests/importerstl_test.js @@ -0,0 +1,204 @@ +var assert = require ('assert'); +var testFiles = require ('../utils/testfiles.js'); +var testUtils = require ('../utils/testutils.js'); + +describe ('Stl Importer', function() { + it ('single_triangle.stl', function () { + var model = testFiles.ImportStlFile ('single_triangle.stl'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'MeshName', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_triangle_with_comments.stl', function () { + var model = testFiles.ImportStlFile ('single_triangle_with_comments.stl'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'MeshName', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('single_triangle_no_normal.stl', function () { + var model = testFiles.ImportStlFile ('single_triangle_no_normal.stl'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'MeshName', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('two_triangles.stl', function () { + var model = testFiles.ImportStlFile ('two_triangles.stl'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'MeshName', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + }, + { + vertices : [0, 0, 1, 1, 0, 1, 1, 1, 1], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); + + it ('stl_ascii.stl', function () { + var model = testFiles.ImportStlFile ('stl_ascii.stl'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'Untitled-5427e5af', + vertexCount : 1716, + normalCount : 572, + uvCount : 0, + triangleCount : 572, + boundingBox : { + min : [0, -1.10792799192095, 0], + max : [4.94407346265022, 3.31831671830375, 1.2] + } + } + ] + }); + }); + + it ('stl_binary.stl', function () { + var model = testFiles.ImportStlFile ('stl_binary.stl'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + vertexCount : 2184, + normalCount : 728, + uvCount : 0, + triangleCount : 728, + boundingBox : { + min : [0, -1.1079280376434326, 0], + max : [5.70156717300415, 3.318316698074341, 1.2000000476837158] + } + } + ] + }); + }); + + + it ('cube_meshlab_ascii.stl', function () { + var model = testFiles.ImportStlFile ('cube_meshlab_ascii.stl'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : 'STL generated by MeshLab', + vertexCount : 36, + normalCount : 12, + uvCount : 0, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); + + it ('cube_meshlab_binary.stl', function () { + var model = testFiles.ImportStlFile ('cube_meshlab_binary.stl'); + assert (OV.CheckModel (model)); + assert.deepStrictEqual (testUtils.ModelToObjectSimple (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + vertexCount : 36, + normalCount : 12, + uvCount : 0, + triangleCount : 12, + boundingBox : { + min : [0, 0, 0], + max : [1, 1, 1] + } + } + ] + }); + }); +}); diff --git a/test/tests/importerutils_test.js b/test/tests/importerutils_test.js new file mode 100644 index 0000000..2b2b511 --- /dev/null +++ b/test/tests/importerutils_test.js @@ -0,0 +1,17 @@ +var assert = require ('assert'); + +describe ('Power of Two', function () { + it ('IsPowerOfTwo', function () { + assert (OV.IsPowerOfTwo (1)); + assert (OV.IsPowerOfTwo (2)); + assert (!OV.IsPowerOfTwo (3)); + for (let i = 4; i <= 1024; i *= 2) { + assert (OV.IsPowerOfTwo (i)); + assert (!OV.IsPowerOfTwo (i + 1)); + assert (!OV.IsPowerOfTwo (i - 1)); + assert.strictEqual (OV.NextPowerOfTwo (i), i); + assert.strictEqual (OV.NextPowerOfTwo (i - 1), i); + assert.strictEqual (OV.NextPowerOfTwo (i + 1), i * 2); + } + }); +}); diff --git a/test/tests/io_test.js b/test/tests/io_test.js new file mode 100644 index 0000000..a97d1de --- /dev/null +++ b/test/tests/io_test.js @@ -0,0 +1,92 @@ +var assert = require ('assert'); +var testFiles = require ('../utils/testfiles.js'); + +describe ('IO Test', function () { + it ('Binary Reader', function () { + let buffer = testFiles.GetArrayBufferFileContent ('bin', 'binary_content.bin'); + let reader = new OV.BinaryReader (buffer, true); + + assert.strictEqual (reader.GetByteLength (), 166); + assert.strictEqual (reader.ReadBoolean8 (), true); + assert.strictEqual (reader.ReadBoolean8 (), false); + + assert.strictEqual (String.fromCharCode (reader.ReadCharacter8 ()), 'a'); + assert.strictEqual (String.fromCharCode (reader.ReadCharacter8 ()), 'A'); + assert.strictEqual (String.fromCharCode (reader.ReadUnsignedCharacter8 ()), 'a'); + assert.strictEqual (String.fromCharCode (reader.ReadUnsignedCharacter8 ()), 'A'); + + assert.strictEqual (reader.ReadInteger16 (), 42); + assert.strictEqual (reader.ReadInteger16 (), -42); + assert.strictEqual (reader.ReadInteger16 (), 32000); + assert.strictEqual (reader.ReadInteger16 (), -32000); + + assert.strictEqual (reader.ReadUnsignedInteger16 (), 42); + assert.strictEqual (reader.ReadUnsignedInteger16 (), 65494); + assert.strictEqual (reader.ReadUnsignedInteger16 (), 32000); + assert.strictEqual (reader.ReadUnsignedInteger16 (), 33536); + + assert.strictEqual (reader.ReadInteger32 (), 42); + assert.strictEqual (reader.ReadInteger32 (), -42); + assert.strictEqual (reader.ReadInteger32 (), 32000); + assert.strictEqual (reader.ReadInteger32 (), -32000); + assert.strictEqual (reader.ReadInteger32 (), 2000000000); + assert.strictEqual (reader.ReadInteger32 (), -2000000000); + + assert.strictEqual (reader.ReadUnsignedInteger32 (), 42); + assert.strictEqual (reader.ReadUnsignedInteger32 (), 4294967254); + assert.strictEqual (reader.ReadUnsignedInteger32 (), 32000); + assert.strictEqual (reader.ReadUnsignedInteger32 (), 4294935296); + assert.strictEqual (reader.ReadUnsignedInteger32 (), 2000000000); + assert.strictEqual (reader.ReadUnsignedInteger32 (), 2294967296); + + assert.strictEqual (reader.ReadInteger32 (), 42); + assert.strictEqual (reader.ReadInteger32 (), -42); + assert.strictEqual (reader.ReadInteger32 (), 32000); + assert.strictEqual (reader.ReadInteger32 (), -32000); + assert.strictEqual (reader.ReadInteger32 (), 2000000000); + assert.strictEqual (reader.ReadInteger32 (), -2000000000); + + assert.strictEqual (reader.ReadUnsignedInteger32 (), 42); + assert.strictEqual (reader.ReadUnsignedInteger32 (), 4294967254); + assert.strictEqual (reader.ReadUnsignedInteger32 (), 32000); + assert.strictEqual (reader.ReadUnsignedInteger32 (), 4294935296); + assert.strictEqual (reader.ReadUnsignedInteger32 (), 2000000000); + assert.strictEqual (reader.ReadUnsignedInteger32 (), 2294967296); + + assert.strictEqual (reader.ReadFloat32 (), 42.0); + assert.strictEqual (reader.ReadFloat32 (), -42.0); + assert.strictEqual (reader.ReadFloat32 (), 12345.6787109375); + assert.strictEqual (reader.ReadFloat32 (), -12345.6787109375); + + assert.strictEqual (reader.ReadDouble64 (), 42.0); + assert.strictEqual (reader.ReadDouble64 (), -42.0); + assert.strictEqual (reader.ReadDouble64 (), 12345.6789); + assert.strictEqual (reader.ReadDouble64 (), -12345.6789); + }); + + it ('Binary Writer', function () { + let writer = new OV.BinaryWriter (27, true); + writer.WriteBoolean8 (true); + writer.WriteCharacter8 (1); + writer.WriteUnsignedCharacter8 (2); + writer.WriteInteger16 (3); + writer.WriteUnsignedInteger16 (4); + writer.WriteInteger32 (5); + writer.WriteUnsignedInteger32 (6); + writer.WriteFloat32 (7.5); + writer.WriteDouble64 (8.5); + assert (writer.End ()); + + let reader = new OV.BinaryReader (writer.GetBuffer (), true); + assert.strictEqual (reader.GetByteLength (), 27); + assert.strictEqual (reader.ReadBoolean8 (), true); + assert.strictEqual (reader.ReadCharacter8 (), 1); + assert.strictEqual (reader.ReadUnsignedCharacter8 (), 2); + assert.strictEqual (reader.ReadInteger16 (), 3); + assert.strictEqual (reader.ReadUnsignedInteger16 (), 4); + assert.strictEqual (reader.ReadInteger32 (), 5); + assert.strictEqual (reader.ReadUnsignedInteger32 (), 6); + assert.strictEqual (reader.ReadFloat32 (), 7.5); + assert.strictEqual (reader.ReadDouble64 (), 8.5); + }); +}); diff --git a/test/tests/mesh_test.js b/test/tests/mesh_test.js new file mode 100644 index 0000000..e00db7a --- /dev/null +++ b/test/tests/mesh_test.js @@ -0,0 +1,109 @@ +var assert = require ('assert'); + +describe ('Mesh', function() { + it ('Default Initialization', function () { + var mesh = new OV.Mesh (); + assert.strictEqual (mesh.GetName (), ''); + assert.strictEqual (mesh.VertexCount (), 0); + assert.strictEqual (mesh.NormalCount (), 0); + assert.strictEqual (mesh.TextureUVCount (), 0); + assert.strictEqual (mesh.TriangleCount (), 0); + }); + + it ('Set Name', function () { + var mesh = new OV.Mesh (); + mesh.SetName ('example'); + assert.strictEqual (mesh.GetName (), 'example'); + }); + + it ('Add Vertex', function () { + var mesh = new OV.Mesh (); + var index = mesh.AddVertex (new OV.Coord3D (1.0, 2.0, 3.0)) + assert.strictEqual (index, 0); + assert.strictEqual (mesh.VertexCount (), 1); + var vertex = mesh.GetVertex (index); + assert.strictEqual (vertex.x, 1.0); + assert.strictEqual (vertex.y, 2.0); + assert.strictEqual (vertex.z, 3.0); + }); + + it ('Add Normal', function () { + var mesh = new OV.Mesh (); + var index = mesh.AddNormal (new OV.Coord3D (1.0, 2.0, 3.0)) + assert.strictEqual (index, 0); + assert.strictEqual (mesh.NormalCount (), 1); + var normal = mesh.GetNormal (index); + assert.strictEqual (normal.x, 1.0); + assert.strictEqual (normal.y, 2.0); + assert.strictEqual (normal.z, 3.0); + }); + + it ('Add Texture UV', function () { + var mesh = new OV.Mesh (); + var index = mesh.AddTextureUV (new OV.Coord2D (1.0, 2.0)) + assert.strictEqual (index, 0); + assert.strictEqual (mesh.TextureUVCount (), 1); + var uv = mesh.GetTextureUV (index); + assert.strictEqual (uv.x, 1.0); + assert.strictEqual (uv.y, 2.0); + }); + + it ('Add Triangle', function () { + var mesh = new OV.Mesh (); + var triangle = new OV.Triangle (1, 2, 3); + var index = mesh.AddTriangle (triangle); + assert.strictEqual (index, 0); + assert.strictEqual (mesh.TriangleCount (), 1); + var triangle = mesh.GetTriangle (index); + assert.strictEqual (triangle.v0, 1); + assert.strictEqual (triangle.v1, 2); + assert.strictEqual (triangle.v2, 3); + assert (triangle.HasVertices ()); + assert (!triangle.HasNormals ()); + assert (!triangle.HasTextureUVs ()); + assert.strictEqual (triangle.n0, null); + assert.strictEqual (triangle.n1, null); + assert.strictEqual (triangle.n2, null); + assert.strictEqual (triangle.u0, null); + assert.strictEqual (triangle.u1, null); + assert.strictEqual (triangle.u2, null); + assert.strictEqual (triangle.mat, null); + }); + + it ('Transform Mesh', function () { + var mesh = new OV.Mesh (); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh.AddNormal (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddTextureUV (new OV.Coord2D (0.0, 0.0)); + mesh.AddTextureUV (new OV.Coord2D (1.0, 0.0)); + mesh.AddTextureUV (new OV.Coord2D (1.0, 1.0)); + var triangle = new OV.Triangle (0, 1, 2); + triangle.SetNormals (0, 0, 0); + triangle.SetTextureUVs (0, 1, 2); + mesh.AddTriangle (triangle); + + let angle = -Math.PI / 2.0; + let rotX = 0.0; + let rotY = 1.0; + let rotZ = 0.0; + + let rotation = [ + Math.sin (angle / 2.0) * rotX, + Math.sin (angle / 2.0) * rotY, + Math.sin (angle / 2.0) * rotZ, + Math.cos (angle / 2.0) + ]; + + let transformation = new OV.Transformation (); + transformation.AppendMatrix (new OV.Matrix ().CreateScale (2.0, 1.0, 1.0)); + transformation.AppendMatrix (new OV.Matrix ().CreateRotation (rotation[0], rotation[1], rotation[2], rotation[3])); + transformation.AppendMatrix (new OV.Matrix ().CreateTranslation (0.0, 0.0, 1.0)); + OV.TransformMesh (mesh, transformation); + assert (OV.CoordIsEqual3D (mesh.GetVertex (0), new OV.Coord3D (0.0, 0.0, 1.0))); + assert (OV.CoordIsEqual3D (mesh.GetVertex (1), new OV.Coord3D (0.0, 0.0, 3.0))); + assert (OV.CoordIsEqual3D (mesh.GetVertex (2), new OV.Coord3D (0.0, 1.0, 3.0))); + assert (OV.CoordIsEqual3D (mesh.GetNormal (0), new OV.Coord3D (-1.0, 0.0, 0.0))); + }); +}); diff --git a/test/tests/meshbuffer_test.js b/test/tests/meshbuffer_test.js new file mode 100644 index 0000000..0520490 --- /dev/null +++ b/test/tests/meshbuffer_test.js @@ -0,0 +1,356 @@ +var assert = require ('assert'); +var testUtils = require ('../utils/testutils.js'); + +describe ('Mesh Buffer', function () { + it ('Mesh To Buffer Simple', function () { + var mesh = new OV.Mesh (); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 1.0)); + mesh.AddNormal (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddTriangle (new OV.Triangle (0, 1, 2).SetNormals (0, 0, 0).SetMaterial (0)); + mesh.AddTriangle (new OV.Triangle (0, 2, 3).SetNormals (0, 0, 0).SetMaterial (0)); + mesh.AddTriangle (new OV.Triangle (4, 5, 6).SetNormals (0, 0, 0).SetMaterial (1)); + mesh.AddTriangle (new OV.Triangle (4, 6, 7).SetNormals (0, 0, 0).SetMaterial (1)); + + let buffer = OV.ConvertMeshToMeshBuffer (mesh); + assert.strictEqual (buffer.primitives.length, 2); + assert.strictEqual (buffer.primitives[0].indices.length, 6); + assert.strictEqual (buffer.primitives[0].vertices.length, 4 * 3); + assert.strictEqual (buffer.primitives[0].normals.length, 4 * 3); + assert.strictEqual (buffer.primitives[0].uvs.length, 0); + assert.strictEqual (buffer.primitives[1].indices.length, 6); + assert.strictEqual (buffer.primitives[1].vertices.length, 4 * 3); + assert.strictEqual (buffer.primitives[1].normals.length, 4 * 3); + assert.strictEqual (buffer.primitives[1].uvs.length, 0); + }); + + it ('Mesh To Buffer Different Normals', function () { + var mesh = new OV.Mesh (); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0)); + mesh.AddNormal (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddNormal (new OV.Coord3D (0.0, 0.0, -1.0)); + mesh.AddTriangle (new OV.Triangle (0, 1, 2).SetNormals (0, 0, 0).SetMaterial (0)); + mesh.AddTriangle (new OV.Triangle (0, 2, 3).SetNormals (1, 1, 1).SetMaterial (0)); + + let buffer = OV.ConvertMeshToMeshBuffer (mesh); + assert.strictEqual (buffer.primitives.length, 1); + assert.strictEqual (buffer.primitives[0].indices.length, 6); + assert.strictEqual (buffer.primitives[0].vertices.length, 6 * 3); + assert.strictEqual (buffer.primitives[0].normals.length, 6 * 3); + assert.strictEqual (buffer.primitives[0].uvs.length, 0); + }); + + it ('Mesh To Buffer Different UVs', function () { + var mesh = new OV.Mesh (); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0)); + mesh.AddNormal (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddTextureUV (new OV.Coord3D (1.0, 1.0)); + mesh.AddTextureUV (new OV.Coord3D (-1.0, -1.0)); + mesh.AddTriangle (new OV.Triangle (0, 1, 2).SetNormals (0, 0, 0).SetTextureUVs (0, 0, 0).SetMaterial (0)); + mesh.AddTriangle (new OV.Triangle (0, 2, 3).SetNormals (0, 0, 0).SetTextureUVs (1, 1, 1).SetMaterial (0)); + + let buffer = OV.ConvertMeshToMeshBuffer (mesh); + assert.strictEqual (buffer.primitives.length, 1); + assert.strictEqual (buffer.primitives[0].indices.length, 6); + assert.strictEqual (buffer.primitives[0].vertices.length, 6 * 3); + assert.strictEqual (buffer.primitives[0].normals.length, 6 * 3); + assert.strictEqual (buffer.primitives[0].uvs.length, 6 * 2); + }); + + it ('Mesh To Buffer Same Normals and UVs', function () { + var mesh = new OV.Mesh (); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0)); + mesh.AddNormal (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddTextureUV (new OV.Coord3D (1.0, 1.0)); + mesh.AddTriangle (new OV.Triangle (0, 1, 2).SetNormals (0, 0, 0).SetTextureUVs (0, 0, 0).SetMaterial (0)); + mesh.AddTriangle (new OV.Triangle (0, 2, 3).SetNormals (0, 0, 0).SetTextureUVs (0, 0, 0).SetMaterial (0)); + + let buffer = OV.ConvertMeshToMeshBuffer (mesh); + assert.strictEqual (buffer.primitives.length, 1); + assert.strictEqual (buffer.primitives[0].indices.length, 6); + assert.strictEqual (buffer.primitives[0].vertices.length, 4 * 3); + assert.strictEqual (buffer.primitives[0].normals.length, 4 * 3); + assert.strictEqual (buffer.primitives[0].uvs.length, 4 * 2); + }); + + it ('Mesh To Buffer Cube', function () { + var model = new OV.Model (); + var mesh = new OV.Mesh (); + + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 1.0)); + + mesh.AddNormal (new OV.Coord3D (0, -1, 0)); + mesh.AddNormal (new OV.Coord3D (1, 0, 0)); + mesh.AddNormal (new OV.Coord3D (0, 1, 0)); + mesh.AddNormal (new OV.Coord3D (-1, 0, 0)); + mesh.AddNormal (new OV.Coord3D (0, 0, -1)); + mesh.AddNormal (new OV.Coord3D (0, 0, 1)); + + mesh.AddTriangle (new OV.Triangle (0, 1, 5).SetNormals (0, 0, 0)); + mesh.AddTriangle (new OV.Triangle (0, 5, 4).SetNormals (0, 0, 0)); + mesh.AddTriangle (new OV.Triangle (1, 2, 6).SetNormals (1, 1, 1)); + mesh.AddTriangle (new OV.Triangle (1, 6, 5).SetNormals (1, 1, 1)); + mesh.AddTriangle (new OV.Triangle (2, 3, 7).SetNormals (2, 2, 2)); + mesh.AddTriangle (new OV.Triangle (2, 7, 6).SetNormals (2, 2, 2)); + mesh.AddTriangle (new OV.Triangle (3, 0, 4).SetNormals (3, 3, 3)); + mesh.AddTriangle (new OV.Triangle (3, 4, 7).SetNormals (3, 3, 3)); + mesh.AddTriangle (new OV.Triangle (0, 3, 2).SetNormals (4, 4, 4)); + mesh.AddTriangle (new OV.Triangle (0, 2, 1).SetNormals (4, 4, 4)); + mesh.AddTriangle (new OV.Triangle (4, 5, 6).SetNormals (5, 5, 5)); + mesh.AddTriangle (new OV.Triangle (4, 6, 7).SetNormals (5, 5, 5)); + + model.AddMesh (mesh); + OV.FinalizeModel (model, function () { + return new OV.Material (); + }); + assert (OV.CheckModel (model)); + + let buffer = OV.ConvertMeshToMeshBuffer (mesh); + assert.strictEqual (buffer.primitives.length, 1); + assert.strictEqual (buffer.primitives[0].indices.length, 36); + assert.strictEqual (buffer.primitives[0].vertices.length, 24 * 3); + assert.strictEqual (buffer.primitives[0].normals.length, 24 * 3); + assert.strictEqual (buffer.primitives[0].uvs.length, 0); + }); + + it ('Mesh To Buffer Cube Auto Normals', function () { + var model = new OV.Model (); + var mesh = new OV.Mesh (); + + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 1.0)); + + mesh.AddTriangle (new OV.Triangle (0, 1, 5)); + mesh.AddTriangle (new OV.Triangle (0, 5, 4)); + mesh.AddTriangle (new OV.Triangle (1, 2, 6)); + mesh.AddTriangle (new OV.Triangle (1, 6, 5)); + mesh.AddTriangle (new OV.Triangle (2, 3, 7)); + mesh.AddTriangle (new OV.Triangle (2, 7, 6)); + mesh.AddTriangle (new OV.Triangle (3, 0, 4)); + mesh.AddTriangle (new OV.Triangle (3, 4, 7)); + mesh.AddTriangle (new OV.Triangle (0, 3, 2)); + mesh.AddTriangle (new OV.Triangle (0, 2, 1)); + mesh.AddTriangle (new OV.Triangle (4, 5, 6)); + mesh.AddTriangle (new OV.Triangle (4, 6, 7)); + + model.AddMesh (mesh); + OV.FinalizeModel (model, function () { + return new OV.Material (); + }); + assert (OV.CheckModel (model)); + + let buffer = OV.ConvertMeshToMeshBuffer (mesh); + assert.strictEqual (buffer.primitives.length, 1); + assert.strictEqual (buffer.primitives[0].indices.length, 36); + assert.strictEqual (buffer.primitives[0].vertices.length, 24 * 3); + assert.strictEqual (buffer.primitives[0].normals.length, 24 * 3); + assert.strictEqual (buffer.primitives[0].uvs.length, 0); + assert.strictEqual (buffer.primitives[0].GetByteLength (2, 4), 36 * 2 + 2 * 24 * 3 * 4); + assert.strictEqual (buffer.GetByteLength (2, 4), 36 * 2 + 2 * 24 * 3 * 4); + }); + + it ('Mesh To Buffer Cube Auto Curve Normals', function () { + var model = new OV.Model (); + var mesh = new OV.Mesh (); + + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 1.0)); + + mesh.AddTriangle (new OV.Triangle (0, 1, 5).SetCurve (1)); + mesh.AddTriangle (new OV.Triangle (0, 5, 4).SetCurve (1)); + mesh.AddTriangle (new OV.Triangle (1, 2, 6).SetCurve (1)); + mesh.AddTriangle (new OV.Triangle (1, 6, 5).SetCurve (1)); + mesh.AddTriangle (new OV.Triangle (2, 3, 7).SetCurve (1)); + mesh.AddTriangle (new OV.Triangle (2, 7, 6).SetCurve (1)); + mesh.AddTriangle (new OV.Triangle (3, 0, 4).SetCurve (1)); + mesh.AddTriangle (new OV.Triangle (3, 4, 7).SetCurve (1)); + mesh.AddTriangle (new OV.Triangle (0, 3, 2).SetCurve (1)); + mesh.AddTriangle (new OV.Triangle (0, 2, 1).SetCurve (1)); + mesh.AddTriangle (new OV.Triangle (4, 5, 6).SetCurve (1)); + mesh.AddTriangle (new OV.Triangle (4, 6, 7).SetCurve (1)); + + model.AddMesh (mesh); + OV.FinalizeModel (model, function () { + return new OV.Material (); + }); + assert (OV.CheckModel (model)); + + let buffer = OV.ConvertMeshToMeshBuffer (mesh); + assert.strictEqual (buffer.primitives.length, 1); + assert.strictEqual (buffer.primitives[0].indices.length, 36); + assert.strictEqual (buffer.primitives[0].vertices.length, 8 * 3); + assert.strictEqual (buffer.primitives[0].normals.length, 8 * 3); + assert.strictEqual (buffer.primitives[0].uvs.length, 0); + assert.strictEqual (buffer.primitives[0].GetByteLength (2, 4), 36 * 2 + 2 * 8 * 3 * 4); + assert.strictEqual (buffer.GetByteLength (2, 4), 36 * 2 + 2 * 8 * 3 * 4); + }); + + it ('Mesh To Buffer Cube Materials', function () { + var model = new OV.Model (); + model.AddMaterial (new OV.Material ()); + model.AddMaterial (new OV.Material ()); + var mesh = new OV.Mesh (); + + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 1.0)); + + mesh.AddTriangle (new OV.Triangle (0, 1, 5).SetMaterial (0)); + mesh.AddTriangle (new OV.Triangle (0, 5, 4).SetMaterial (0)); + mesh.AddTriangle (new OV.Triangle (1, 2, 6).SetMaterial (0)); + mesh.AddTriangle (new OV.Triangle (1, 6, 5).SetMaterial (0)); + mesh.AddTriangle (new OV.Triangle (2, 3, 7).SetMaterial (0)); + mesh.AddTriangle (new OV.Triangle (2, 7, 6).SetMaterial (0)); + mesh.AddTriangle (new OV.Triangle (3, 0, 4).SetMaterial (1)); + mesh.AddTriangle (new OV.Triangle (3, 4, 7).SetMaterial (1)); + mesh.AddTriangle (new OV.Triangle (0, 3, 2).SetMaterial (1)); + mesh.AddTriangle (new OV.Triangle (0, 2, 1).SetMaterial (1)); + mesh.AddTriangle (new OV.Triangle (4, 5, 6).SetMaterial (1)); + mesh.AddTriangle (new OV.Triangle (4, 6, 7).SetMaterial (1)); + + model.AddMesh (mesh); + OV.FinalizeModel (model, function () { + return new OV.Material (); + }); + assert (OV.CheckModel (model)); + + let buffer = OV.ConvertMeshToMeshBuffer (mesh); + assert.strictEqual (buffer.primitives.length, 2); + assert.strictEqual (buffer.primitives[0].indices.length, 18); + assert.strictEqual (buffer.primitives[0].vertices.length, 12 * 3); + assert.strictEqual (buffer.primitives[0].normals.length, 12 * 3); + assert.strictEqual (buffer.primitives[0].uvs.length, 0); + assert.strictEqual (buffer.primitives[0].GetByteLength (2, 4), 18 * 2 + 2 * 12 * 3 * 4); + assert.strictEqual (buffer.primitives[1].indices.length, 18); + assert.strictEqual (buffer.primitives[1].vertices.length, 12 * 3); + assert.strictEqual (buffer.primitives[1].normals.length, 12 * 3); + assert.strictEqual (buffer.primitives[1].uvs.length, 0); + assert.strictEqual (buffer.primitives[1].GetByteLength (2, 4), 18 * 2 + 2 * 12 * 3 * 4); + assert.strictEqual (buffer.GetByteLength (2, 4), 36 * 2 + 2 * 24 * 3 * 4); + }); + + it ('Mesh To Buffer Cube One UV', function () { + var model = new OV.Model (); + var mesh = new OV.Mesh (); + + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 1.0)); + mesh.AddTextureUV (new OV.Coord2D (0.0, 0.0)); + mesh.AddTextureUV (new OV.Coord2D (1.0, 0.0)); + mesh.AddTextureUV (new OV.Coord2D (1.0, 1.0)); + + mesh.AddTriangle (new OV.Triangle (0, 1, 5)); + mesh.AddTriangle (new OV.Triangle (0, 5, 4)); + mesh.AddTriangle (new OV.Triangle (1, 2, 6).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (1, 6, 5)); + mesh.AddTriangle (new OV.Triangle (2, 3, 7)); + mesh.AddTriangle (new OV.Triangle (2, 7, 6)); + mesh.AddTriangle (new OV.Triangle (3, 0, 4)); + mesh.AddTriangle (new OV.Triangle (3, 4, 7)); + mesh.AddTriangle (new OV.Triangle (0, 3, 2)); + mesh.AddTriangle (new OV.Triangle (0, 2, 1)); + mesh.AddTriangle (new OV.Triangle (4, 5, 6)); + mesh.AddTriangle (new OV.Triangle (4, 6, 7)); + + model.AddMesh (mesh); + OV.FinalizeModel (model, function () { + return new OV.Material (); + }); + assert (OV.CheckModel (model)); + + let buffer = OV.ConvertMeshToMeshBuffer (mesh); + assert.strictEqual (buffer.primitives.length, 1); + assert.strictEqual (buffer.primitives[0].indices.length, 36); + assert.strictEqual (buffer.primitives[0].vertices.length, 25 * 3); + assert.strictEqual (buffer.primitives[0].normals.length, 25 * 3); + assert.strictEqual (buffer.primitives[0].uvs.length, 25 * 2); + }); + + it ('Mesh To Buffer Cube All UVs', function () { + var model = new OV.Model (); + var mesh = new OV.Mesh (); + + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 1.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 1.0, 1.0)); + mesh.AddTextureUV (new OV.Coord2D (0.0, 0.0)); + mesh.AddTextureUV (new OV.Coord2D (1.0, 0.0)); + mesh.AddTextureUV (new OV.Coord2D (1.0, 1.0)); + + mesh.AddTriangle (new OV.Triangle (0, 1, 5).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (0, 5, 4).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (1, 2, 6).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (1, 6, 5).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (2, 3, 7).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (2, 7, 6).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (3, 0, 4).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (3, 4, 7).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (0, 3, 2).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (0, 2, 1).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (4, 5, 6).SetTextureUVs (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (4, 6, 7).SetTextureUVs (0, 1, 2)); + + model.AddMesh (mesh); + OV.FinalizeModel (model, function () { + return new OV.Material (); + }); + assert (OV.CheckModel (model)); + + let buffer = OV.ConvertMeshToMeshBuffer (mesh); + assert.strictEqual (buffer.primitives.length, 1); + assert.strictEqual (buffer.primitives[0].indices.length, 36); + assert.strictEqual (buffer.primitives[0].vertices.length, 30 * 3); + assert.strictEqual (buffer.primitives[0].normals.length, 30 * 3); + assert.strictEqual (buffer.primitives[0].uvs.length, 30 * 2); + }); +}); diff --git a/test/tests/model_test.js b/test/tests/model_test.js new file mode 100644 index 0000000..3a617f7 --- /dev/null +++ b/test/tests/model_test.js @@ -0,0 +1,152 @@ +var assert = require ('assert'); + +describe ('Model', function() { + it ('Default Initialization', function () { + var model = new OV.Model (); + assert.strictEqual (model.MaterialCount (), 0); + assert.strictEqual (model.MeshCount (), 0); + }); + + it ('Add Material', function () { + var model = new OV.Model (); + var material = new OV.Material (); + material.name = 'example'; + var index = model.AddMaterial (material); + assert.strictEqual (model.MaterialCount (), 1); + var theMaterial = model.GetMaterial (index); + assert.strictEqual (theMaterial.name, 'example'); + }); + + it ('Add Mesh', function () { + var model = new OV.Model (); + var mesh = new OV.Mesh (); + mesh.SetName ('example'); + var index = model.AddMesh (mesh); + assert.strictEqual (model.MeshCount (), 1); + var theMesh = model.GetMesh (index); + assert.strictEqual (theMesh.GetName (), 'example'); + }); + + it ('Add Mesh To Index', function () { + var model = new OV.Model (); + var mesh1 = new OV.Mesh (); + var mesh2 = new OV.Mesh (); + var mesh3 = new OV.Mesh (); + var mesh4 = new OV.Mesh (); + mesh1.SetName ('A'); + mesh2.SetName ('B'); + mesh3.SetName ('C'); + mesh4.SetName ('D'); + model.AddMesh (mesh1); + model.AddMesh (mesh2); + model.AddMeshToIndex (mesh3, 1); + model.AddMeshToIndex (mesh4, 3); + assert.strictEqual (model.GetMesh (0).GetName (), 'A'); + assert.strictEqual (model.GetMesh (1).GetName (), 'C'); + assert.strictEqual (model.GetMesh (2).GetName (), 'B'); + assert.strictEqual (model.GetMesh (3).GetName (), 'D'); + }); + + it ('Counters', function () { + var model = new OV.Model (); + let mesh = new OV.Mesh (); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddNormal (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddNormal (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh.AddTextureUV (new OV.Coord2D (0.0, 0.0)); + mesh.AddTriangle (new OV.Triangle (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (0, 1, 2)); + mesh.AddTriangle (new OV.Triangle (0, 1, 2)); + model.AddMesh (mesh); + assert.strictEqual (model.VertexCount (), 3); + assert.strictEqual (model.NormalCount (), 2); + assert.strictEqual (model.TextureUVCount (), 1); + assert.strictEqual (model.TriangleCount (), 4); + }); + + it ('Remove Mesh', function () { + var model = new OV.Model (); + + let mesh1 = new OV.Mesh (); + mesh1.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh1.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh1.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh1.AddTriangle (new OV.Triangle (0, 1, 2)); + model.AddMesh (mesh1); + + let mesh2 = new OV.Mesh (); + mesh2.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh2.AddVertex (new OV.Coord3D (1.0, 0.0, 1.0)); + mesh2.AddVertex (new OV.Coord3D (1.0, 1.0, 1.0)); + mesh2.AddTriangle (new OV.Triangle (0, 1, 2)); + model.AddMesh (mesh2); + + assert.strictEqual (model.MeshCount (), 2); + assert.strictEqual (model.VertexCount (), 6); + assert.strictEqual (model.TriangleCount (), 2); + + model.RemoveMesh (0); + assert.strictEqual (model.MeshCount (), 1); + assert.strictEqual (model.VertexCount (), 3); + assert.strictEqual (model.TriangleCount (), 1); + + model.RemoveMesh (0); + assert.strictEqual (model.MeshCount (), 0); + assert.strictEqual (model.VertexCount (), 0); + assert.strictEqual (model.TriangleCount (), 0); + }); +}); + +describe ('Model Finalization', function() { + it ('Calculate Normal', function () { + var mesh = new OV.Mesh (); + var v0 = mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + var v1 = mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + var v2 = mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + var triangleIndex = mesh.AddTriangle (new OV.Triangle (v0, v1, v2)); + var model = new OV.Model (); + var meshIndex = model.AddMesh (mesh); + assert.strictEqual (model.MaterialCount (), 0); + OV.FinalizeModel (model, function () { new OV.Material () }); + assert.strictEqual (model.MaterialCount (), 1); + var theMesh = model.GetMesh (meshIndex); + assert.strictEqual (theMesh.NormalCount (), 1); + var triangle = theMesh.GetTriangle (triangleIndex); + assert.strictEqual (triangle.n0, 0); + assert.strictEqual (triangle.n1, 0); + assert.strictEqual (triangle.n2, 0); + assert.strictEqual (triangle.mat, 0); + }); + + it ('Calculate Curved Normal', function () { + var mesh = new OV.Mesh (); + var v0 = mesh.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + var v1 = mesh.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + var v2 = mesh.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + var v3 = mesh.AddVertex (new OV.Coord3D (0.0, 0.0, -1.0)); + + var triangle1 = new OV.Triangle (v0, v1, v2); + triangle1.curve = 1; + var triangle2 = new OV.Triangle (v0, v3, v1); + triangle2.curve = 1; + + var triangle1 = mesh.AddTriangle (triangle1); + var triangle1 = mesh.AddTriangle (triangle2); + + var model = new OV.Model () + var meshIndex = model.AddMesh (mesh); + + OV.FinalizeModel (model, function () { new OV.Material () }); + + var theMesh = model.GetMesh (meshIndex); + assert.strictEqual (theMesh.NormalCount (), 6); + + var normal = theMesh.GetNormal (0); + assert.strictEqual (normal.x, 0.0); + assert.strictEqual (normal.y, -0.7071067811865475); + assert.strictEqual (normal.z, 0.7071067811865475); + }); +}); diff --git a/test/tests/modelutils_test.js b/test/tests/modelutils_test.js new file mode 100644 index 0000000..b1bc681 --- /dev/null +++ b/test/tests/modelutils_test.js @@ -0,0 +1,81 @@ +var assert = require ('assert'); +var testUtils = require ('../utils/testutils.js'); + +describe ('Model Utils', function () { + it ('Create Merged Model', function () { + var model = new OV.Model (); + + var mesh1 = new OV.Mesh (); + mesh1.AddVertex (new OV.Coord3D (0.0, 0.0, 0.0)); + mesh1.AddVertex (new OV.Coord3D (1.0, 0.0, 0.0)); + mesh1.AddVertex (new OV.Coord3D (1.0, 1.0, 0.0)); + mesh1.AddTriangle (new OV.Triangle (0, 1, 2)); + model.AddMesh (mesh1); + + var mesh2 = new OV.Mesh (); + mesh2.AddVertex (new OV.Coord3D (0.0, 0.0, 1.0)); + mesh2.AddVertex (new OV.Coord3D (1.0, 0.0, 1.0)); + mesh2.AddVertex (new OV.Coord3D (1.0, 1.0, 1.0)); + mesh2.AddTriangle (new OV.Triangle (0, 1, 2)); + model.AddMesh (mesh2); + + OV.FinalizeModel (model, function () { return new OV.Material (); }); + assert.deepStrictEqual (testUtils.ModelToObject (model), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + }, + { + name : '', + triangles : [ + { + vertices : [0, 0, 1, 1, 0, 1, 1, 1, 1], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + + var mergedModel = OV.CreateMergedModel (model); + assert.deepStrictEqual (testUtils.ModelToObject (mergedModel), { + name : '', + materials : [ + { name : '' } + ], + meshes : [ + { + name : '', + triangles : [ + { + vertices : [0, 0, 0, 1, 0, 0, 1, 1, 0], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + }, + { + vertices : [0, 0, 1, 1, 0, 1, 1, 1, 1], + normals : [0, 0, 1, 0, 0, 1, 0, 0, 1], + uvs : [], + mat : 0 + } + ] + } + ] + }); + }); +}); diff --git a/test/tests/taskrunner_test.js b/test/tests/taskrunner_test.js new file mode 100644 index 0000000..3b70d87 --- /dev/null +++ b/test/tests/taskrunner_test.js @@ -0,0 +1,31 @@ +var assert = require ('assert'); + +describe ('Task Runner', function () { + it ('Run task zero times', function () { + var tr = new OV.TaskRunner (); + var numbers = []; + tr.Run (0, { + runTask : function (index, ready) { + numbers.push (index); + ready (); + }, + onReady : function () { + assert.deepStrictEqual (numbers, []); + } + }); + }); + + it ('Run task three times', function () { + var tr = new OV.TaskRunner (); + var numbers = []; + tr.Run (3, { + runTask : function (index, ready) { + numbers.push (index); + ready (); + }, + onReady : function () { + assert.deepStrictEqual (numbers, [0, 1, 2]); + } + }); + }); +}); diff --git a/test/utils/importall.js b/test/utils/importall.js new file mode 100644 index 0000000..0f0421e --- /dev/null +++ b/test/utils/importall.js @@ -0,0 +1,10 @@ +var path = require ('path'); +var nativeImport = require ('../framework/native_import.js'); + +var config = require ('../../tools/config.json') +var importerFiles = config['importer_files']; +var fileIndex, filePath; +for (fileIndex = 0; fileIndex < importerFiles.length; fileIndex++) { + filePath = importerFiles[fileIndex]; + nativeImport.AddNativeSourceFile (path.join ('..', filePath)); +} diff --git a/test/utils/testfiles.js b/test/utils/testfiles.js new file mode 100644 index 0000000..ed4cd2d --- /dev/null +++ b/test/utils/testfiles.js @@ -0,0 +1,104 @@ +var path = require ('path'); +var fs = require ('fs'); +var testUtils = require ('./testutils.js') + +module.exports = +{ + GetTextFileContent : function (folder, fileName) + { + var testFilePath = path.join ('testfiles', folder, fileName); + return fs.readFileSync (testFilePath).toString (); + }, + + GetArrayBufferFileContent : function (folder, fileName) + { + var testFilePath = path.join ('testfiles', folder, fileName); + var buffer = fs.readFileSync (testFilePath); + var arrayBuffer = new ArrayBuffer (buffer.length); + var uint8Array = new Uint8Array (arrayBuffer); + var i; + for (i = 0; i < buffer.length; ++i) { + uint8Array[i] = buffer[i]; + } + return arrayBuffer + }, + + ImportObjFile : function (fileName) + { + var importer = new OV.ImporterObj (); + return this.ImportFile (importer, OV.FileFormat.Text, 'obj', fileName); + }, + + ImportStlFile : function (fileName) + { + var importer = new OV.ImporterStl (); + return this.ImportFile (importer, OV.FileFormat.Binary, 'stl', fileName); + }, + + ImportOffFile : function (fileName) + { + var importer = new OV.ImporterOff (); + return this.ImportFile (importer, OV.FileFormat.Text, 'off', fileName); + }, + + ImportPlyFile : function (fileName) + { + var importer = new OV.ImporterPly (); + return this.ImportFile (importer, OV.FileFormat.Binary, 'ply', fileName); + }, + + Import3dsFile : function (fileName) + { + var importer = new OV.Importer3ds (); + return this.ImportFile (importer, OV.FileFormat.Binary, '3ds', fileName); + }, + + ImportGltfFile : function (folderName, fileName) + { + let extension = OV.GetFileExtension (fileName); + let format = OV.FileFormat.Text; + if (extension == 'glb') { + format = OV.FileFormat.Binary; + } + var importer = new OV.ImporterGltf (); + return this.ImportFile (importer, format, 'gltf/' + folderName, fileName); + }, + + ImportFile : function (importer, format, folder, fileName) + { + var myThis = this; + var content = null; + if (format == OV.FileFormat.Text) { + content = this.GetTextFileContent (folder, fileName); + } else if (format == OV.FileFormat.Binary) { + content = this.GetArrayBufferFileContent (folder, fileName); + } + var extension = OV.GetFileExtension (fileName); + importer.Import (content, extension, { + getDefaultMaterial : function () { + var material = new OV.Material (); + return material; + }, + getFileContent : function (requestedFileName) { + let extension = OV.GetFileExtension (requestedFileName); + let knownFormats = importer.GetKnownFileFormats (); + let format = OV.FileFormat.Binary; + if (knownFormats[extension] !== undefined) { + format = knownFormats[extension]; + } + let fileContent = null; + if (format == OV.FileFormat.Text) { + fileContent = myThis.GetTextFileContent (folder, requestedFileName); + } else if (format == OV.FileFormat.Binary) { + fileContent = myThis.GetArrayBufferFileContent (folder, requestedFileName); + } + return fileContent; + }, + getTextureObjectUrl : function (requestedFileName) { + return requestedFileName; + } + }); + let model = importer.GetModel (); + return model; + } +} diff --git a/test/utils/testutils.js b/test/utils/testutils.js new file mode 100644 index 0000000..cd23992 --- /dev/null +++ b/test/utils/testutils.js @@ -0,0 +1,114 @@ +module.exports = +{ + ModelToObject : function (model) + { + var obj = { + name : model.GetName (), + materials : [], + meshes : [] + }; + + var i, j; + + var material; + for (i = 0; i < model.MaterialCount (); i++) { + material = model.GetMaterial (i); + obj.materials.push ({ + name : material.name + }); + } + + var mesh, triangle, meshObj, triangleObj; + for (i = 0; i < model.MeshCount (); i++) { + mesh = model.GetMesh (i); + meshObj = { + name : mesh.GetName (), + triangles : [] + }; + for (j = 0; j < mesh.TriangleCount (); j++) { + triangle = mesh.GetTriangle (j); + triangleObj = { + mat : triangle.mat, + vertices : [], + normals : [], + uvs : [] + }; + triangleObj.vertices.push ( + mesh.GetVertex (triangle.v0).x, + mesh.GetVertex (triangle.v0).y, + mesh.GetVertex (triangle.v0).z, + mesh.GetVertex (triangle.v1).x, + mesh.GetVertex (triangle.v1).y, + mesh.GetVertex (triangle.v1).z, + mesh.GetVertex (triangle.v2).x, + mesh.GetVertex (triangle.v2).y, + mesh.GetVertex (triangle.v2).z + ); + triangleObj.normals.push ( + mesh.GetNormal (triangle.n0).x, + mesh.GetNormal (triangle.n0).y, + mesh.GetNormal (triangle.n0).z, + mesh.GetNormal (triangle.n1).x, + mesh.GetNormal (triangle.n1).y, + mesh.GetNormal (triangle.n1).z, + mesh.GetNormal (triangle.n2).x, + mesh.GetNormal (triangle.n2).y, + mesh.GetNormal (triangle.n2).z + ); + if (triangle.HasTextureUVs ()) { + triangleObj.uvs.push ( + mesh.GetTextureUV (triangle.u0).x, + mesh.GetTextureUV (triangle.u0).y, + mesh.GetTextureUV (triangle.u1).x, + mesh.GetTextureUV (triangle.u1).y, + mesh.GetTextureUV (triangle.u2).x, + mesh.GetTextureUV (triangle.u2).y + ); + } + meshObj.triangles.push (triangleObj); + } + obj.meshes.push (meshObj); + } + + return obj; + }, + + ModelToObjectSimple : function (model) + { + var obj = { + name : model.GetName (), + materials : [], + meshes : [] + }; + + var i; + + var material; + for (i = 0; i < model.MaterialCount (); i++) { + material = model.GetMaterial (i); + obj.materials.push ({ + name : material.name + }); + } + + var mesh, meshObj, boundingBox; + for (i = 0; i < model.MeshCount (); i++) { + mesh = model.GetMesh (i); + boundingBox = OV.GetMeshBoundingBox (mesh); + meshObj = { + name : mesh.GetName (), + vertexCount : mesh.VertexCount (), + normalCount : mesh.NormalCount (), + uvCount : mesh.TextureUVCount (), + triangleCount : mesh.TriangleCount (), + boundingBox : { + min : [boundingBox[0].x, boundingBox[0].y, boundingBox[0].z], + max : [boundingBox[1].x, boundingBox[1].y, boundingBox[1].z] + } + }; + obj.meshes.push (meshObj); + } + + return obj; + } +} diff --git a/tools/config.json b/tools/config.json new file mode 100644 index 0000000..348826c --- /dev/null +++ b/tools/config.json @@ -0,0 +1,65 @@ +{ + "lib_files" : [ + "libs/jquery-3.5.1.min.js", + "libs/three.min-126.js" + ], + "importer_files" : [ + "source/core/core.js", + "source/core/taskrunner.js", + "source/geometry/geometry.js", + "source/geometry/coord2d.js", + "source/geometry/coord3d.js", + "source/geometry/matrix.js", + "source/geometry/transformation.js", + "source/geometry/tween.js", + "source/io/binaryreader.js", + "source/io/binarywriter.js", + "source/io/textwriter.js", + "source/io/bufferutils.js", + "source/io/fileutils.js", + "source/model/modelentities.js", + "source/model/mesh.js", + "source/model/meshbuffer.js", + "source/model/model.js", + "source/model/modelutils.js", + "source/model/modelfinalization.js", + "source/import/importerutils.js", + "source/import/importerbase.js", + "source/import/importerobj.js", + "source/import/importerstl.js", + "source/import/importeroff.js", + "source/import/importerply.js", + "source/import/importer3ds.js", + "source/import/importergltf.js", + "source/import/importer.js", + "source/export/exporterutils.js", + "source/export/exporterbase.js", + "source/export/exporterobj.js", + "source/export/exporterstl.js", + "source/export/exporterply.js", + "source/export/exporteroff.js", + "source/export/exportergltf.js", + "source/export/exporter.js", + "source/external/three.converter.js", + "source/external/three.model.loader.js", + "source/viewer/domutils.js", + "source/viewer/navigation.js", + "source/viewer/viewer.js", + "source/viewer/domviewer.js" + ], + "website_files" : [ + "website/o3dv/utils.js", + "website/o3dv/toolbar.js", + "website/o3dv/treeview.js", + "website/o3dv/modal.js", + "website/o3dv/dialogs.js", + "website/o3dv/modeldata.js", + "website/o3dv/info.js", + "website/o3dv/menu.js", + "website/o3dv/hashhandler.js", + "website/o3dv/website.css", + "website/o3dv/loader.js", + "website/o3dv/website.js", + "website/o3dv/embed.js" + ] +} diff --git a/tools/create_package.py b/tools/create_package.py new file mode 100644 index 0000000..87538c9 --- /dev/null +++ b/tools/create_package.py @@ -0,0 +1,148 @@ +import os +import sys +import shutil +import subprocess +import zipfile +import json +import re + +from lib import tools_lib as Tools + +def PrintInfo (message): + print ('INFO: ' + message) + +def PrintError (message): + print ('ERROR: ' + message) + +def GetVersion (rootDir): + packageJson = None + with open (os.path.join (rootDir, 'package.json')) as packageJsonFile: + packageJson = json.load (packageJsonFile) + return packageJson['version'] + +def JSHintFolder (folder): + result = Tools.RunCommand (['jshint', folder]) + if result != 0: + return False + return True + +def CompressFiles (inputFiles, outputFile): + parameters = [] + parameters.append ('google-closure-compiler') + for inputFile in inputFiles: + extension = os.path.splitext (inputFile)[1] + if extension == '.js': + parameters.append ('--js=' + os.path.join ('..', inputFile)) + parameters.append ('--js_output_file=' + outputFile) + result = Tools.RunCommand (parameters) + if result != 0: + return False + return True + +def CreateDestinationDir (config, rootDir, websiteDir, version): + if not os.path.exists (websiteDir): + os.makedirs (websiteDir) + + webSourcesDir = os.path.join (websiteDir, 'o3dv') + if not os.path.exists (webSourcesDir): + os.makedirs (webSourcesDir) + + shutil.copy2 (os.path.join (rootDir, 'website', 'index.html'), websiteDir) + shutil.copy2 (os.path.join (rootDir, 'website', 'embed.html'), websiteDir) + shutil.copy2 (os.path.join (rootDir, 'website', 'robots.txt'), websiteDir) + shutil.copy2 (os.path.join (rootDir, 'website', 'o3dv', 'website.css'), os.path.join (webSourcesDir, 'o3dv.website.css')) + shutil.copytree (os.path.join (rootDir, 'libs'), os.path.join (websiteDir, 'libs')) + shutil.copytree (os.path.join (rootDir, 'website', 'assets'), os.path.join (websiteDir, 'assets')) + shutil.copytree (os.path.join (rootDir, 'website', 'info'), os.path.join (websiteDir, 'info')) + + libFiles = config['lib_files'] + importerFiles = ['o3dv/o3dv.min.js'] + websiteFiles = [ + 'o3dv/o3dv.website.css', + 'o3dv/o3dv.website.min.js' + ] + + linePrefix = '\t' + for htmlFileName in ['index.html', 'embed.html', os.path.join ('info', 'index.html')]: + htmlFilePath = os.path.join (websiteDir, htmlFileName) + replacer = Tools.TokenReplacer (htmlFilePath, False) + replacer.ReplaceTokenFileLinks ('', '', libFiles, None) + replacer.ReplaceTokenFileLinks ('', '', importerFiles, version) + replacer.ReplaceTokenFileLinks ('', '', websiteFiles, version) + metaFile = os.path.join (rootDir, 'tools', 'website_meta_data.txt') + if os.path.exists (metaFile): + metaContent = Tools.GetFileContent (metaFile) + replacer.ReplaceTokenContent ('', '', metaContent) + analyticsFile = os.path.join (rootDir, 'tools', 'website_analytics_data.txt') + if os.path.exists (analyticsFile): + analyticsContent = Tools.GetFileContent (analyticsFile) + replacer.ReplaceTokenContent ('', '', analyticsContent) + scriptFile = os.path.join (rootDir, 'tools', 'website_script_data.txt') + if os.path.exists (scriptFile): + scriptContent = Tools.GetFileContent (scriptFile) + replacer.ReplaceTokenContent ('', '', scriptContent) + replacer.WriteToFile (htmlFilePath) + +def CreatePackage (websiteDir, packageDir, version): + if not os.path.exists (packageDir): + os.makedirs (packageDir) + + zipPath = os.path.join (packageDir, 'o3dv_' + version + '.zip') + zip = zipfile.ZipFile (zipPath, mode = 'w', compression = zipfile.ZIP_DEFLATED) + zip.write (os.path.join (websiteDir, 'libs', 'three.min-126.js'), 'three.min.js') + zip.write (os.path.join (websiteDir, 'o3dv', 'o3dv.min.js'), 'o3dv.min.js') + zip.close () + return True + +def Main (argv): + currentDir = os.path.dirname (os.path.abspath (__file__)) + os.chdir (currentDir) + + rootDir = os.path.abspath ('..') + buildDir = os.path.join (rootDir, 'build') + websiteDir = os.path.join (buildDir, 'website') + packageDir = os.path.join (buildDir, 'package') + if os.path.exists (buildDir): + shutil.rmtree (buildDir) + + config = None + with open ('config.json') as configJson: + config = json.load (configJson) + + PrintInfo ('JSHint importer sources.') + jsHintResult = JSHintFolder (os.path.join (rootDir, 'source')) + if not jsHintResult: + PrintError ('JSHint importer sources failed.') + return 1 + + PrintInfo ('JSHint website sources.') + jsHintResult = JSHintFolder (os.path.join (rootDir, 'website', 'o3dv')) + if not jsHintResult: + PrintError ('JSHint website sources failed.') + return 1 + + version = GetVersion (rootDir) + PrintInfo ('Create build directory') + CreateDestinationDir (config, rootDir, websiteDir, version) + + PrintInfo ('Compress importer sources.') + compressResult = CompressFiles (config['importer_files'], os.path.join (websiteDir, 'o3dv', 'o3dv.min.js')) + if not compressResult: + PrintError ('Compress importer sources failed.') + return 1 + + PrintInfo ('Compress website sources.') + compressResult = CompressFiles (config['website_files'], os.path.join (websiteDir, 'o3dv', 'o3dv.website.min.js')) + if not compressResult: + PrintError ('Compress website sources failed.') + return 1 + + PrintInfo ('Create package.') + packageResult = CreatePackage (websiteDir, packageDir, version) + if not packageResult: + PrintError ('Create package failed.') + return 1 + + return 0 + +sys.exit (Main (sys.argv)) diff --git a/tools/generate_test_obj.py b/tools/generate_test_obj.py new file mode 100644 index 0000000..c1c7ab2 --- /dev/null +++ b/tools/generate_test_obj.py @@ -0,0 +1,192 @@ +import os +import sys +import subprocess +import json +import math + +from lib import tools_lib as Tools + +class ObjWriter: + def __init__ (self): + self.content = '' + self.offsetX = 0.0 + self.offsetY = 0.0 + self.offsetZ = 0.0 + self.scale = 1.0 + self.vertexCount = 0 + self.vertexOffset = 0 + + def GetContent (self): + return self.content + + def SetOffset (self, x, y, z): + self.offsetX = x + self.offsetY = y + self.offsetZ = z + + def SetScale (self, scale): + self.scale = scale + + def AddMesh (self, name): + self.AddLine ('g ' + name) + self.vertexOffset = self.vertexCount + + def AddVertex (self, x, y, z): + self.AddLine ('v ' + ' '.join ([str (x * self.scale + self.offsetX), str (y * self.scale + self.offsetY), str (z * self.scale + self.offsetZ)])) + self.vertexCount += 1 + + def AddFace (self, vertices): + objVertices = [] + for vertex in vertices: + objVertices.append (str (vertex + self.vertexOffset + 1)) + self.AddLine ('f ' + ' '.join (objVertices)) + + def AddLine (self, line): + self.content += line + '\n' + +def DistFromOrigin (x, y, z): + return math.sqrt (x * x + y * y + z * z) + +def Main (argv): + currentDir = os.path.dirname (os.path.abspath (__file__)) + os.chdir (currentDir) + + ow = ObjWriter () + + ow.AddMesh ('Tetrahedron') + a = 1.0 + ow.SetOffset (0.0, 0.0, 0.0) + ow.SetScale (1.0 / DistFromOrigin (a, a, a)) + ow.AddVertex (+a, +a, +a) + ow.AddVertex (-a, -a, +a) + ow.AddVertex (-a, +a, -a) + ow.AddVertex (+a, -a, -a) + ow.AddFace ([0, 1, 3]) + ow.AddFace ([0, 2, 1]) + ow.AddFace ([0, 3, 2]) + ow.AddFace ([1, 2, 3]) + + ow.AddMesh ('Hexahedron') + a = 1.0 + ow.SetOffset (0.0, 3.0, 0.0) + ow.SetScale (1.0 / DistFromOrigin (a, a, a)) + ow.AddVertex (+a, +a, +a) + ow.AddVertex (+a, +a, -a) + ow.AddVertex (+a, -a, +a) + ow.AddVertex (+a, -a, -a) + ow.AddVertex (-a, +a, +a) + ow.AddVertex (-a, +a, -a) + ow.AddVertex (-a, -a, +a) + ow.AddVertex (-a, -a, -a) + ow.AddFace ([0, 1, 5, 4]) + ow.AddFace ([0, 2, 3, 1]) + ow.AddFace ([0, 4, 6, 2]) + ow.AddFace ([1, 3, 7, 5]) + ow.AddFace ([2, 6, 7, 3]) + ow.AddFace ([4, 5, 7, 6]) + + ow.AddMesh ('Octahedron') + a = 1.0 + b = 0.0 + ow.SetOffset (3.0, 0.0, 0.0) + ow.SetScale (1.0 / DistFromOrigin (a, b, b)) + ow.AddVertex (+a, +b, +b) + ow.AddVertex (-a, +b, +b) + ow.AddVertex (+b, +a, +b) + ow.AddVertex (+b, -a, +b) + ow.AddVertex (+b, +b, +a) + ow.AddVertex (+b, +b, -a) + ow.AddFace ([0, 2, 4]) + ow.AddFace ([0, 3, 5]) + ow.AddFace ([0, 4, 3]) + ow.AddFace ([0, 5, 2]) + ow.AddFace ([1, 2, 5]) + ow.AddFace ([1, 3, 4]) + ow.AddFace ([1, 4, 2]) + ow.AddFace ([1, 5, 3]) + + ow.AddMesh ('Dodecahedron') + a = 1.0 + b = 0.0 + c = (1.0 + math.sqrt (5.0)) / 2.0 + d = 1.0 / c + ow.SetOffset (3.0, 3.0, 0.0) + ow.SetScale (1.0 / DistFromOrigin (a, a, a)) + ow.AddVertex (+a, +a, +a) + ow.AddVertex (+a, +a, -a) + ow.AddVertex (+a, -a, +a) + ow.AddVertex (-a, +a, +a) + ow.AddVertex (+a, -a, -a) + ow.AddVertex (-a, +a, -a) + ow.AddVertex (-a, -a, +a) + ow.AddVertex (-a, -a, -a) + ow.AddVertex (+b, +d, +c) + ow.AddVertex (+b, +d, -c) + ow.AddVertex (+b, -d, +c) + ow.AddVertex (+b, -d, -c) + ow.AddVertex (+d, +c, +b) + ow.AddVertex (+d, -c, +b) + ow.AddVertex (-d, +c, +b) + ow.AddVertex (-d, -c, +b) + ow.AddVertex (+c, +b, +d) + ow.AddVertex (-c, +b, +d) + ow.AddVertex (+c, +b, -d) + ow.AddVertex (-c, +b, -d) + ow.AddFace ([0, 8, 10, 2, 16]) + ow.AddFace ([0, 16, 18, 1, 12]) + ow.AddFace ([0, 12, 14, 3, 8]) + ow.AddFace ([1, 9, 5, 14, 12]) + ow.AddFace ([1, 18, 4, 11, 9]) + ow.AddFace ([2, 10, 6, 15, 13]) + ow.AddFace ([2, 13, 4, 18, 16]) + ow.AddFace ([3, 14, 5, 19, 17]) + ow.AddFace ([3, 17, 6, 10, 8]) + ow.AddFace ([4, 13, 15, 7, 11]) + ow.AddFace ([5, 9, 11, 7, 19]) + ow.AddFace ([6, 17, 19, 7, 15]) + + ow.AddMesh ('Icosahedron') + a = 1.0 + b = 0.0 + c = (1.0 + math.sqrt (5.0)) / 2.0 + ow.SetOffset (6.0, 1.5, 0.0) + ow.SetScale (1.0 / DistFromOrigin (b, a, c)) + ow.AddVertex (+b, +a, +c) + ow.AddVertex (+b, +a, -c) + ow.AddVertex (+b, -a, +c) + ow.AddVertex (+b, -a, -c) + ow.AddVertex (+a, +c, +b) + ow.AddVertex (+a, -c, +b) + ow.AddVertex (-a, +c, +b) + ow.AddVertex (-a, -c, +b) + ow.AddVertex (+c, +b, +a) + ow.AddVertex (+c, +b, -a) + ow.AddVertex (-c, +b, +a) + ow.AddVertex (-c, +b, -a) + ow.AddFace ([0, 2, 8]) + ow.AddFace ([0, 4, 6]) + ow.AddFace ([0, 6, 10]) + ow.AddFace ([0, 8, 4]) + ow.AddFace ([0, 10, 2]) + ow.AddFace ([1, 3, 11]) + ow.AddFace ([1, 4, 9]) + ow.AddFace ([1, 6, 4]) + ow.AddFace ([1, 9, 3]) + ow.AddFace ([1, 11, 6]) + ow.AddFace ([2, 5, 8]) + ow.AddFace ([2, 7, 5]) + ow.AddFace ([2, 10, 7]) + ow.AddFace ([3, 5, 7]) + ow.AddFace ([3, 7, 11]) + ow.AddFace ([3, 9, 5]) + ow.AddFace ([4, 8, 9]) + ow.AddFace ([5, 9, 8]) + ow.AddFace ([6, 11, 10]) + ow.AddFace ([7, 10, 11]) + + print (ow.GetContent ()) + Tools.WriteContentToFile ('solids.obj', ow.GetContent ()) + + return 0 + +sys.exit (Main (sys.argv)) diff --git a/tools/lib/tools_lib.py b/tools/lib/tools_lib.py new file mode 100644 index 0000000..c8f8337 --- /dev/null +++ b/tools/lib/tools_lib.py @@ -0,0 +1,111 @@ +import codecs +import os +import subprocess + +def GetFileContent (filePath): + fileObject = codecs.open (filePath, 'r', 'utf-8') + content = fileObject.read () + fileObject.close () + return content + +def WriteContentToFile (filePath, content): + fileObject = codecs.open (filePath, 'w', 'utf-8') + fileObject.write (content) + fileObject.close () + +def RunCommand (commands): + process = subprocess.Popen (commands, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True) + out, err = process.communicate () + if process.returncode != 0: + print (out.decode ()) + print (err.decode ()) + return process.returncode + +class TokenReplacer: + def __init__ (self, filePath, keepToken): + self.fileContent = GetFileContent (filePath) + self.eolChar = self.GetEOLChar () + self.keepToken = keepToken + + def ReplaceTokenContent (self, begToken, endToken, content): + begPosition, linePrefix = self.GetTokenBegPosition (begToken) + endPosition = self.GetTokenEndPosition (endToken) + if begPosition == -1 or endPosition == -1: + return + newContentLines = [] + for contentLine in content.splitlines (): + newContentLines.append (linePrefix + contentLine) + newContent = self.eolChar.join (newContentLines) + self.ReplaceContent (begToken, endToken, begPosition, endPosition + len (endToken), linePrefix, newContent) + + def ReplaceTokenFileLinks (self, begToken, endToken, fileUrls, version): + begPosition, linePrefix = self.GetTokenBegPosition (begToken) + endPosition = self.GetTokenEndPosition (endToken) + if begPosition == -1 or endPosition == -1: + return + fileLinks = [] + for fileUrl in fileUrls: + fileSourceUrl = fileUrl + if version != None: + fileSourceUrl += '?v=' + version + extension = os.path.splitext (fileUrl)[1] + if (extension == '.js'): + fileLinks.append (linePrefix + '') + elif (extension == '.css'): + fileLinks.append (linePrefix + '') + newContent = self.eolChar.join (fileLinks) + self.ReplaceContent (begToken, endToken, begPosition, endPosition + len (endToken), linePrefix, newContent) + + def WriteToFile (self, filePath): + WriteContentToFile (filePath, self.fileContent) + + def ReplaceContent (self, begToken, endToken, begPosition, endPosition, linePrefix, newContent): + if self.keepToken: + newContent = linePrefix + begToken + self.eolChar + newContent + self.eolChar + linePrefix + endToken + self.fileContent = self.fileContent.replace (self.fileContent[begPosition : endPosition], newContent) + + def GetTokenBegPosition (self, begToken): + begPosition = self.fileContent.find (begToken) + linePrefix = '' + if begPosition == -1: + return -1, '' + while begPosition > 0: + if self.fileContent[begPosition - 1] == '\n': + break + begPosition -= 1 + linePrefix = self.fileContent[begPosition] + linePrefix + return begPosition, linePrefix + + def GetTokenEndPosition (self, endToken): + return self.fileContent.find (endToken) + + def GetEOLChar (self): + if self.fileContent.count ('\r\n') > 0: + return '\r\n' + else: + return '\n' + +def ReplaceInFile (filePath, begToken, endToken, newContent): + content = GetFileContent (filePath) + begPosition = content.find (begToken) + endPosition = content.find (endToken) + if begPosition == -1 or endPosition == -1: + return + content = content.replace (content[begPosition : endPosition + len (endToken)], newContent) + WriteContentToFile (filePath, content) + +def CreateFileLinks (fileUrls, linePrefix, eolChar): + result = '' + for fileUrl in fileUrls: + extension = os.path.splitext (fileUrl)[1] + if (extension == '.js'): + result += linePrefix + '' + eolChar + elif (extension == '.css'): + result += linePrefix + '' + eolChar + return result + +def CreateFileList (fileList, replaceFrom, replaceTo): + result = [] + for item in fileList: + result.append (item.replace (replaceFrom, replaceTo)) + return result diff --git a/tools/optimize_svg_files.py b/tools/optimize_svg_files.py new file mode 100644 index 0000000..60ef502 --- /dev/null +++ b/tools/optimize_svg_files.py @@ -0,0 +1,15 @@ +import os +import sys +import subprocess +import json + +from lib import tools_lib as Tools + +def Main (argv): + currentDir = os.path.dirname (os.path.abspath (__file__)) + os.chdir (currentDir) + imagesPath = os.path.abspath (os.path.join ('..', 'website', 'assets', 'images')) + for i in range (0, 5): + Tools.RunCommand (['svgo', '-r', imagesPath]) + +sys.exit (Main (sys.argv)) diff --git a/tools/sandbox/index.html b/tools/sandbox/index.html new file mode 100644 index 0000000..d85c8a6 --- /dev/null +++ b/tools/sandbox/index.html @@ -0,0 +1,63 @@ + + + + + + + + Online 3D Viewer + + + + + + + + + + diff --git a/tools/update_includes.py b/tools/update_includes.py new file mode 100644 index 0000000..99ff3c9 --- /dev/null +++ b/tools/update_includes.py @@ -0,0 +1,34 @@ +import os +import sys +import subprocess +import json + +from lib import tools_lib as Tools + +def PrintInfo (message): + print ('INFO: ' + message) + +def PrintError (message): + print ('ERROR: ' + message) + +def Main (argv): + currentDir = os.path.dirname (os.path.abspath (__file__)) + os.chdir (currentDir) + + config = None + with open ('config.json') as configJson: + config = json.load (configJson) + + rootDir = os.path.abspath ('..') + for htmlFileName in ['index.html', 'embed.html', 'embed_selfhost.html']: + htmlFilePath = os.path.join (rootDir, 'website', htmlFileName) + replacer = Tools.TokenReplacer (htmlFilePath, True) + libFiles = Tools.CreateFileList (config['lib_files'], 'libs/', '../libs/') + importerFiles = Tools.CreateFileList (config['importer_files'], 'source/', '../source/') + websiteFiles = Tools.CreateFileList (config['website_files'], 'website/', '') + replacer.ReplaceTokenFileLinks ('', '', libFiles, None) + replacer.ReplaceTokenFileLinks ('', '', importerFiles, None) + replacer.ReplaceTokenFileLinks ('', '', websiteFiles, None) + replacer.WriteToFile (htmlFilePath) + +sys.exit (Main (sys.argv)) diff --git a/website/assets/fonts/Quicksand/OFL.txt b/website/assets/fonts/Quicksand/OFL.txt new file mode 100644 index 0000000..5186b46 --- /dev/null +++ b/website/assets/fonts/Quicksand/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2011 The Quicksand Project Authors (https://github.com/andrew-paglinawan/QuicksandFamily), with Reserved Font Name “Quicksand”. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/website/assets/fonts/Quicksand/Quicksand-Bold.ttf b/website/assets/fonts/Quicksand/Quicksand-Bold.ttf new file mode 100644 index 0000000..edfa93f Binary files /dev/null and b/website/assets/fonts/Quicksand/Quicksand-Bold.ttf differ diff --git a/website/assets/fonts/Quicksand/Quicksand-Light.ttf b/website/assets/fonts/Quicksand/Quicksand-Light.ttf new file mode 100644 index 0000000..42ef072 Binary files /dev/null and b/website/assets/fonts/Quicksand/Quicksand-Light.ttf differ diff --git a/website/assets/fonts/Quicksand/Quicksand-Medium.ttf b/website/assets/fonts/Quicksand/Quicksand-Medium.ttf new file mode 100644 index 0000000..7eadfad Binary files /dev/null and b/website/assets/fonts/Quicksand/Quicksand-Medium.ttf differ diff --git a/website/assets/fonts/Quicksand/Quicksand-Regular.ttf b/website/assets/fonts/Quicksand/Quicksand-Regular.ttf new file mode 100644 index 0000000..cb1596d Binary files /dev/null and b/website/assets/fonts/Quicksand/Quicksand-Regular.ttf differ diff --git a/website/assets/fonts/Quicksand/Quicksand-SemiBold.ttf b/website/assets/fonts/Quicksand/Quicksand-SemiBold.ttf new file mode 100644 index 0000000..b280a9d Binary files /dev/null and b/website/assets/fonts/Quicksand/Quicksand-SemiBold.ttf differ diff --git a/website/assets/images/3dviewer_net_favicon.png b/website/assets/images/3dviewer_net_favicon.png new file mode 100644 index 0000000..b7a3526 Binary files /dev/null and b/website/assets/images/3dviewer_net_favicon.png differ diff --git a/website/assets/images/3dviewer_net_logo.svg b/website/assets/images/3dviewer_net_logo.svg new file mode 100644 index 0000000..47de5f4 --- /dev/null +++ b/website/assets/images/3dviewer_net_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/3dviewer_net_logo_social.png b/website/assets/images/3dviewer_net_logo_social.png new file mode 100644 index 0000000..6e5a336 Binary files /dev/null and b/website/assets/images/3dviewer_net_logo_social.png differ diff --git a/website/assets/images/3dviewer_net_logo_social.svg b/website/assets/images/3dviewer_net_logo_social.svg new file mode 100644 index 0000000..41d0099 --- /dev/null +++ b/website/assets/images/3dviewer_net_logo_social.svg @@ -0,0 +1,278 @@ + +image/svg+xml \ No newline at end of file diff --git a/website/assets/images/3dviewer_net_logo_text.svg b/website/assets/images/3dviewer_net_logo_text.svg new file mode 100644 index 0000000..a8fd738 --- /dev/null +++ b/website/assets/images/3dviewer_net_logo_text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/dialog/arrow_down.svg b/website/assets/images/dialog/arrow_down.svg new file mode 100644 index 0000000..bd5d33d --- /dev/null +++ b/website/assets/images/dialog/arrow_down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/dialog/file_download.svg b/website/assets/images/dialog/file_download.svg new file mode 100644 index 0000000..858b06c --- /dev/null +++ b/website/assets/images/dialog/file_download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/header/donate.svg b/website/assets/images/header/donate.svg new file mode 100644 index 0000000..428d2e9 --- /dev/null +++ b/website/assets/images/header/donate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/header/donate_hover.svg b/website/assets/images/header/donate_hover.svg new file mode 100644 index 0000000..919a877 --- /dev/null +++ b/website/assets/images/header/donate_hover.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/header/feedback.svg b/website/assets/images/header/feedback.svg new file mode 100644 index 0000000..9aa4830 --- /dev/null +++ b/website/assets/images/header/feedback.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/header/feedback_hover.svg b/website/assets/images/header/feedback_hover.svg new file mode 100644 index 0000000..12dfeb7 --- /dev/null +++ b/website/assets/images/header/feedback_hover.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/header/github.svg b/website/assets/images/header/github.svg new file mode 100644 index 0000000..b7779c8 --- /dev/null +++ b/website/assets/images/header/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/header/github_hover.svg b/website/assets/images/header/github_hover.svg new file mode 100644 index 0000000..1a7ddc8 --- /dev/null +++ b/website/assets/images/header/github_hover.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/header/info.svg b/website/assets/images/header/info.svg new file mode 100644 index 0000000..9aa4830 --- /dev/null +++ b/website/assets/images/header/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/header/info_hover.svg b/website/assets/images/header/info_hover.svg new file mode 100644 index 0000000..12dfeb7 --- /dev/null +++ b/website/assets/images/header/info_hover.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/toolbar/embed.svg b/website/assets/images/toolbar/embed.svg new file mode 100644 index 0000000..a0d09e9 --- /dev/null +++ b/website/assets/images/toolbar/embed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/toolbar/export.svg b/website/assets/images/toolbar/export.svg new file mode 100644 index 0000000..f63e483 --- /dev/null +++ b/website/assets/images/toolbar/export.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/toolbar/fit.svg b/website/assets/images/toolbar/fit.svg new file mode 100644 index 0000000..08e471b --- /dev/null +++ b/website/assets/images/toolbar/fit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/toolbar/fix_up_off.svg b/website/assets/images/toolbar/fix_up_off.svg new file mode 100644 index 0000000..c5541a2 --- /dev/null +++ b/website/assets/images/toolbar/fix_up_off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/toolbar/fix_up_on.svg b/website/assets/images/toolbar/fix_up_on.svg new file mode 100644 index 0000000..29da0e7 --- /dev/null +++ b/website/assets/images/toolbar/fix_up_on.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/toolbar/flip.svg b/website/assets/images/toolbar/flip.svg new file mode 100644 index 0000000..b3cce76 --- /dev/null +++ b/website/assets/images/toolbar/flip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/toolbar/open.svg b/website/assets/images/toolbar/open.svg new file mode 100644 index 0000000..3b042f1 --- /dev/null +++ b/website/assets/images/toolbar/open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/toolbar/open_url.svg b/website/assets/images/toolbar/open_url.svg new file mode 100644 index 0000000..3a6e6d2 --- /dev/null +++ b/website/assets/images/toolbar/open_url.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/toolbar/up_y.svg b/website/assets/images/toolbar/up_y.svg new file mode 100644 index 0000000..6150547 --- /dev/null +++ b/website/assets/images/toolbar/up_y.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/toolbar/up_z.svg b/website/assets/images/toolbar/up_z.svg new file mode 100644 index 0000000..f911e29 --- /dev/null +++ b/website/assets/images/toolbar/up_z.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/arrow_down.svg b/website/assets/images/tree/arrow_down.svg new file mode 100644 index 0000000..bd5d33d --- /dev/null +++ b/website/assets/images/tree/arrow_down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/arrow_up.svg b/website/assets/images/tree/arrow_up.svg new file mode 100644 index 0000000..91fa0b8 --- /dev/null +++ b/website/assets/images/tree/arrow_up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/details.svg b/website/assets/images/tree/details.svg new file mode 100644 index 0000000..73d2180 --- /dev/null +++ b/website/assets/images/tree/details.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/files.svg b/website/assets/images/tree/files.svg new file mode 100644 index 0000000..0109be9 --- /dev/null +++ b/website/assets/images/tree/files.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/fit_tree.svg b/website/assets/images/tree/fit_tree.svg new file mode 100644 index 0000000..90c6300 --- /dev/null +++ b/website/assets/images/tree/fit_tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/hidden.svg b/website/assets/images/tree/hidden.svg new file mode 100644 index 0000000..7df38b3 --- /dev/null +++ b/website/assets/images/tree/hidden.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/materials.svg b/website/assets/images/tree/materials.svg new file mode 100644 index 0000000..847df77 --- /dev/null +++ b/website/assets/images/tree/materials.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/meshes.svg b/website/assets/images/tree/meshes.svg new file mode 100644 index 0000000..f796218 --- /dev/null +++ b/website/assets/images/tree/meshes.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/missing_files.svg b/website/assets/images/tree/missing_files.svg new file mode 100644 index 0000000..f3d4174 --- /dev/null +++ b/website/assets/images/tree/missing_files.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/model.svg b/website/assets/images/tree/model.svg new file mode 100644 index 0000000..b8a968b --- /dev/null +++ b/website/assets/images/tree/model.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/open.svg b/website/assets/images/tree/open.svg new file mode 100644 index 0000000..dbccf3e --- /dev/null +++ b/website/assets/images/tree/open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/images/tree/visible.svg b/website/assets/images/tree/visible.svg new file mode 100644 index 0000000..e84fe2a --- /dev/null +++ b/website/assets/images/tree/visible.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/website/assets/models/logo.mtl b/website/assets/models/logo.mtl new file mode 100644 index 0000000..87eddba --- /dev/null +++ b/website/assets/models/logo.mtl @@ -0,0 +1,14 @@ +newmtl Blue_1 +Ka 0.000000 0.000000 0.000000 +Kd 0.392156 0.752941 1.000000 +Ks 0.500000 0.500000 0.500000 + +newmtl Blue_2 +Ka 0.000000 0.000000 0.000000 +Kd 0.274509 0.533333 0.705882 +Ks 0.500000 0.500000 0.500000 + +newmtl Blue_3 +Ka 0.000000 0.000000 0.000000 +Kd 0.160784 0.305882 0.403921 +Ks 0.500000 0.500000 0.500000 diff --git a/website/assets/models/logo.obj b/website/assets/models/logo.obj new file mode 100644 index 0000000..a52e932 --- /dev/null +++ b/website/assets/models/logo.obj @@ -0,0 +1,39 @@ +# 3dviewer.net logo + +mtllib logo.mtl + +v 0 0 0 +v 0 0 2 +v 1 0 2 +v 1 0 0 +v 2 0 2 +v 2 0 1 +v 1 0 1 +v 2 2 2 +v 2 2 1 +v 2 2 0 +v 2 1 0 +v 2 1 1 +v 1 2 0 +v 0 2 0 +v 1 1 0 +v 1 1 1 + +o Outer +usemtl Blue_1 +f 1 2 3 4 +f 3 5 6 7 +usemtl Blue_2 +f 5 8 9 6 +f 9 10 11 12 +usemtl Blue_3 +f 1 4 13 14 +f 10 13 15 11 + +o Inner +usemtl Blue_2 +f 4 7 16 15 +usemtl Blue_3 +f 6 12 16 7 +usemtl Blue_1 +f 11 15 16 12 diff --git a/website/assets/models/solids.mtl b/website/assets/models/solids.mtl new file mode 100644 index 0000000..39caf84 --- /dev/null +++ b/website/assets/models/solids.mtl @@ -0,0 +1,24 @@ +newmtl Red +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.200000 0.200000 +Ks 0.500000 0.500000 0.500000 + +newmtl Green +Ka 0.000000 0.000000 0.000000 +Kd 0.152940 0.607840 0.380390 +Ks 0.500000 0.500000 0.500000 + +newmtl Blue +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.541180 0.721570 +Ks 0.500000 0.500000 0.500000 + +newmtl Orange +Ka 0.000000 0.000000 0.000000 +Kd 1.000000 0.800000 0.200000 +Ks 0.500000 0.500000 0.500000 + +newmtl Purple +Ka 0.000000 0.000000 0.000000 +Kd 0.800000 0.400000 0.600000 +Ks 0.500000 0.500000 0.500000 diff --git a/website/assets/models/solids.obj b/website/assets/models/solids.obj new file mode 100644 index 0000000..0d2ef40 --- /dev/null +++ b/website/assets/models/solids.obj @@ -0,0 +1,116 @@ +mtllib solids.mtl + +g Tetrahedron +usemtl Red +v 0.5773502691896258 0.5773502691896258 0.5773502691896258 +v -0.5773502691896258 -0.5773502691896258 0.5773502691896258 +v -0.5773502691896258 0.5773502691896258 -0.5773502691896258 +v 0.5773502691896258 -0.5773502691896258 -0.5773502691896258 +f 1 2 4 +f 1 3 2 +f 1 4 3 +f 2 3 4 + +g Hexahedron +usemtl Green +v 0.5773502691896258 3.5773502691896257 0.5773502691896258 +v 0.5773502691896258 3.5773502691896257 -0.5773502691896258 +v 0.5773502691896258 2.4226497308103743 0.5773502691896258 +v 0.5773502691896258 2.4226497308103743 -0.5773502691896258 +v -0.5773502691896258 3.5773502691896257 0.5773502691896258 +v -0.5773502691896258 3.5773502691896257 -0.5773502691896258 +v -0.5773502691896258 2.4226497308103743 0.5773502691896258 +v -0.5773502691896258 2.4226497308103743 -0.5773502691896258 +f 5 6 10 9 +f 5 7 8 6 +f 5 9 11 7 +f 6 8 12 10 +f 7 11 12 8 +f 9 10 12 11 + +g Octahedron +usemtl Blue +v 4.0 0.0 0.0 +v 2.0 0.0 0.0 +v 3.0 1.0 0.0 +v 3.0 -1.0 0.0 +v 3.0 0.0 1.0 +v 3.0 0.0 -1.0 +f 13 15 17 +f 13 16 18 +f 13 17 16 +f 13 18 15 +f 14 15 18 +f 14 16 17 +f 14 17 15 +f 14 18 16 + +g Dodecahedron +usemtl Orange +v 3.5773502691896257 3.5773502691896257 0.5773502691896258 +v 3.5773502691896257 3.5773502691896257 -0.5773502691896258 +v 3.5773502691896257 2.4226497308103743 0.5773502691896258 +v 2.4226497308103743 3.5773502691896257 0.5773502691896258 +v 3.5773502691896257 2.4226497308103743 -0.5773502691896258 +v 2.4226497308103743 3.5773502691896257 -0.5773502691896258 +v 2.4226497308103743 2.4226497308103743 0.5773502691896258 +v 2.4226497308103743 2.4226497308103743 -0.5773502691896258 +v 3.0 3.35682208977309 0.9341723589627159 +v 3.0 3.35682208977309 -0.9341723589627159 +v 3.0 2.64317791022691 0.9341723589627159 +v 3.0 2.64317791022691 -0.9341723589627159 +v 3.35682208977309 3.9341723589627158 0.0 +v 3.35682208977309 2.0658276410372842 0.0 +v 2.64317791022691 3.9341723589627158 0.0 +v 2.64317791022691 2.0658276410372842 0.0 +v 3.9341723589627158 3.0 0.35682208977308993 +v 2.0658276410372842 3.0 0.35682208977308993 +v 3.9341723589627158 3.0 -0.35682208977308993 +v 2.0658276410372842 3.0 -0.35682208977308993 +f 19 27 29 21 35 +f 19 35 37 20 31 +f 19 31 33 22 27 +f 20 28 24 33 31 +f 20 37 23 30 28 +f 21 29 25 34 32 +f 21 32 23 37 35 +f 22 33 24 38 36 +f 22 36 25 29 27 +f 23 32 34 26 30 +f 24 28 30 26 38 +f 25 36 38 26 34 + +g Icosahedron +usemtl Purple +v 6.0 2.0257311121191335 0.85065080835204 +v 6.0 2.0257311121191335 -0.85065080835204 +v 6.0 0.9742688878808664 0.85065080835204 +v 6.0 0.9742688878808664 -0.85065080835204 +v 6.525731112119134 2.35065080835204 0.0 +v 6.525731112119134 0.64934919164796 0.0 +v 5.474268887880866 2.35065080835204 0.0 +v 5.474268887880866 0.64934919164796 0.0 +v 6.85065080835204 1.5 0.5257311121191336 +v 6.85065080835204 1.5 -0.5257311121191336 +v 5.14934919164796 1.5 0.5257311121191336 +v 5.14934919164796 1.5 -0.5257311121191336 +f 39 41 47 +f 39 43 45 +f 39 45 49 +f 39 47 43 +f 39 49 41 +f 40 42 50 +f 40 43 48 +f 40 45 43 +f 40 48 42 +f 40 50 45 +f 41 44 47 +f 41 46 44 +f 41 49 46 +f 42 44 46 +f 42 46 50 +f 42 48 44 +f 43 47 48 +f 44 48 47 +f 45 50 49 +f 46 49 50 diff --git a/website/embed.html b/website/embed.html new file mode 100644 index 0000000..6407e96 --- /dev/null +++ b/website/embed.html @@ -0,0 +1,108 @@ + + + + + + + + + Online 3D Viewer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + diff --git a/website/embed_iframe.html b/website/embed_iframe.html new file mode 100644 index 0000000..2c4258b --- /dev/null +++ b/website/embed_iframe.html @@ -0,0 +1,35 @@ + + + + + + + + + Online 3D Viewer + + + + + + + + + + diff --git a/website/embed_selfhost.html b/website/embed_selfhost.html new file mode 100644 index 0000000..71e94fd --- /dev/null +++ b/website/embed_selfhost.html @@ -0,0 +1,80 @@ + + + + + + + + + Online 3D Viewer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + diff --git a/website/examples.html b/website/examples.html new file mode 100644 index 0000000..06199d4 --- /dev/null +++ b/website/examples.html @@ -0,0 +1,24 @@ + + + + + + + + + Online 3D Viewer + + + + + + + diff --git a/website/index.html b/website/index.html new file mode 100644 index 0000000..738c6bd --- /dev/null +++ b/website/index.html @@ -0,0 +1,149 @@ + + + + + + + + + Online 3D Viewer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Drag and drop your 3D models here.
+ Supported formats: obj, 3ds, stl, ply, gltf, glb, off. +
+
+ Or you can use the browse button above. +
+
+
Example models:
+
+ logo + solids +
+
+
+ + + diff --git a/website/info/index.html b/website/info/index.html new file mode 100644 index 0000000..4970819 --- /dev/null +++ b/website/info/index.html @@ -0,0 +1,232 @@ + + + + + + + + + Online 3D Viewer Manual + + + + + + + + + + + + +
+

Online 3D Viewer Manual

+

+ This is the user manual for the 3dviewer.net website. + The website can open several 3D file formats and visualize the model in your browser. + Supported file formats: obj, 3ds, stl, ply, gltf, glb, and off. +

+

+

    +
  1. Supported file formats
  2. +
  3. Loading models
  4. +
      +
    1. Loading models from your computer
    2. +
    3. Loading models hosted on a web server
    4. +
    5. Missing files
    6. +
    +
  5. Embedding the viewer
  6. +
      +
    1. Embedding models hosted on GitHub
    2. +
    3. Embedding models hosted on DropBox
    4. +
    5. Self-hosted viewer
    6. +
    +
  7. Exporting models
  8. +
+

+ +

Supported file formats

+

+ The website supports several file formats for import and export. If a file format has an ascii and binary version, + usually it's recommended to use the binary version. During export an ascii file is created. +

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FormatImportExportComment
objasciiwith mtl and texture support
3dsbinarywith texture support
stlasciibinary version is preferred
binary-
plyasciibinary version is preferred
binarywith vertex and face color support
gltfascii (.gltf)version 2.0
binary (.glb)version 2.0
offascii-
binary-
+

+ +

Loading models

+

+ There are several ways to load models. You can use files from your computer or load files hosted on an external web server. +

+

+ Please note, that some 3D models are built up from multiple files (for example an obj file usually comes with an mtl file, + and some texture files). For the correct visualization you have to import all of the required files. See the Missing files + section for more details. +

+ +

Loading models from your computer

+

+ You can load models from your computer. In this case the model won't be uploaded to any web server, + the whole visualization happens in your browser. +

+

+ There are two ways to load models from your computer: +

    +
  1. + Use the open button on the toolbar (). Here you can select all the + relevant files. +
  2. +
  3. Drag and drop all the relevant files into the browser window.
  4. +
+

+ +

Loading models hosted on a web server

+

+ You can load models by url with the open url button on the toolbar (). + Here you can list all of the model files. You should place every file in a separate line. +

+

+ To make this work, CORS (Cross-Origin Resource Sharing) must be enabled on the server. +

+ +

Missing files

+

+ Sometimes you see missing files in the tree (). It means that your model + refers to another files, but they are not selected to import. To solve the issue you have to import the model again together with the referenced + files, or just import the missing files on their own. +

+ +

Embedding the viewer

+

+ The website allows you to embed a 3D model in your page. It works only if your models are hosted on a web server. + To get the embedding code, click on the embedding button () in the toolbar. +

+ +

Embedding models hosted on GitHub

+

+ To host your models on GitHub follow the steps below: +

+

+

    +
  1. Commit models to GitHub with all the required files.
  2. +
  3. Go to 3dviewer.net, and click on the open url button in the toolbar ().
  4. +
  5. + Open the files on GitHub, copy the link of the file from the address bar, and paste it in the dialog. See the example below. +
    + https://github.com/kovacsv/Online3DViewer/blob/master/embeddable/testfiles/textured%20cube.obj
    + https://github.com/kovacsv/Online3DViewer/blob/master/embeddable/testfiles/textured%20cube.mtl
    + https://github.com/kovacsv/Online3DViewer/blob/master/embeddable/testfiles/texture%20with%20space.png +
    +
  6. +
  7. Click on OK, and if the model is loaded, click on the embed button in the toolbar ().
  8. +
+

+ +

Embedding models hosted on DropBox

+

+ To host your models on DropBox follow the steps below: +

+

+

    +
  1. Upload models to DropBox with all the required files.
  2. +
  3. Go to 3dviewer.net, and click on the open url button in the toolbar ().
  4. +
  5. + Get the sharing link from DropBox for all of the files, and paste it in the dialog. See the example below. +
    + https://www.dropbox.com/s/wd6lt7h4xa8fuv0/cube_four_instances.3ds?dl=0
    + https://www.dropbox.com/s/6dfk1jveevbofxm/texture.png?dl=0 +
    +
  6. +
  7. Click on OK, and if the model is loaded, click on the embed button in the toolbar ().
  8. +
+

+ +

Self-hosted viewer

+

+ You can host the viewer and the models on your website. + See the developer documentation for details. +

+ +

Exporting models

+

+ You can export your model in several file formats. To achieve this, click on the export button in the toolbar (). + After that select the format you would like to export to, and download the resulting files one by one. +

+ +
+ + + diff --git a/website/info/info.css b/website/info/info.css new file mode 100644 index 0000000..9cf5435 --- /dev/null +++ b/website/info/info.css @@ -0,0 +1,153 @@ +@font-face +{ + font-family: Quicksand; + src: url('../assets/fonts/Quicksand/Quicksand-Regular.ttf'); +} + +html, body +{ + color: #000000; + background: #ffffff; + font-size: 16px; + font-family: Quicksand, Helvetica, sans-serif; + scroll-behavior: smooth +} + +a +{ + color: #3393bd; + text-decoration: none; +} + +a:hover +{ + text-decoration: underline; +} + +img.inline +{ + width: 18px; + height: 18px; + display: inline-block; + margin-left: 3px; + margin-right: 3px; + margin-bottom: -4px; +} + +h1 +{ + font-size: 27px; + font-weight: normal; + margin-top: 30px; + margin-bottom: 20px; + padding: 10px 0px; + border-bottom: 1px solid #dddddd; +} + +h1 a +{ + color: inherit; +} + +h1 a:hover +{ + text-decoration: none; +} + +h2 +{ + font-size: 25px; + font-weight: normal; + margin-top: 30px; + margin-bottom: 20px; + padding: 10px 0px; + border-bottom: 1px solid #dddddd; +} + +h3 +{ + font-size: 21px; + font-weight: normal; + margin-top: 30px; + margin-bottom: 20px; +} + +p +{ + margin: 10px 0px; + line-height: 25px; +} + +p.info +{ + background: #e4f4ff; + padding: 10px; + border-left: 3px solid #3393bd; +} + +ol, ul +{ + padding: 0px 25px; +} + +li +{ + margin: 10px 0px; +} + +table +{ + border-collapse: collapse; +} + +table td, th +{ + padding: 10px; + border: 1px solid #dddddd; +} + +table th +{ + background: #fafafa; +} + +table td.center +{ + text-align: center; +} + +table td.green +{ + background: #eeffee; +} + +table td.red +{ + background: #ffeeee; +} + +div.main +{ + width: 920px; + margin: 30px auto; +} + +div.code +{ + background: #f0f0f0; + font-family: monospace; + font-size: 14px; + margin: 10px 0px; + padding : 5px; +} + +@media only screen and (max-width: 1000px) +{ + +div.main +{ + width: auto; + margin: 0px 20px; +} + +} diff --git a/website/o3dv/dialogs.js b/website/o3dv/dialogs.js new file mode 100644 index 0000000..af4a83f --- /dev/null +++ b/website/o3dv/dialogs.js @@ -0,0 +1,373 @@ +OV.ProgressDialog = class +{ + constructor () + { + this.modal = new OV.Modal (); + this.modal.SetCloseable (false); + this.imageDiv = null; + this.textDiv = null; + } + + SetText (text) + { + this.textDiv.html (text); + } + + Show (text) + { + let contentDiv = this.modal.GetContentDiv (); + contentDiv.addClass ('ov_progress'); + + this.imageDiv = $('').addClass ('ov_progress_img').attr ('src', 'assets/images/3dviewer_net_logo.svg').appendTo (contentDiv); + this.textDiv = $('
').addClass ('ov_progress_text').appendTo (contentDiv); + + this.SetText (text); + this.modal.Open (); + } + + Hide () + { + this.modal.Close (); + } +}; + +OV.ButtonDialog = class +{ + constructor () + { + this.modal = new OV.Modal (); + } + + Init (title, buttons) + { + function AddButton (button, buttonsDiv) + { + let buttonDiv = $('
').addClass ('ov_dialog_button').html (button.name).appendTo (buttonsDiv); + if (button.subClass) { + buttonDiv.addClass (button.subClass); + } + buttonDiv.click (function () { + button.onClick (); + }); + } + + let contentDiv = this.modal.GetContentDiv (); + contentDiv.addClass ('ov_dialog'); + + let titleDiv = $('
').addClass ('ov_dialog_title').html (title).appendTo (contentDiv); + let dialogContentDiv = $('
').addClass ('ov_dialog_content').appendTo (contentDiv); + let buttonsDiv = $('
').addClass ('ov_dialog_buttons').appendTo (contentDiv); + let buttonsInnerDiv = $('
').addClass ('ov_dialog_buttons_inner').appendTo (buttonsDiv); + for (let i = 0; i < buttons.length; i++) { + AddButton (buttons[i], buttonsInnerDiv); + } + + return dialogContentDiv; + } + + Show () + { + this.modal.Open (); + } + + Hide () + { + this.modal.Close (); + } +}; + +OV.ListPopup = class +{ + constructor () + { + this.modal = new OV.Modal (); + this.listDiv = null; + } + + Init () + { + let contentDiv = this.modal.GetContentDiv (); + contentDiv.addClass ('ov_popup'); + this.listDiv = $('
').addClass ('ov_popup_list').addClass ('ov_thin_scrollbar').appendTo (contentDiv); + } + + SetCustomResizeHandler (customResizeHandler) + { + this.modal.SetCustomResizeHandler (customResizeHandler); + } + + AddListItem (item, callbacks) + { + let listItemDiv = $('
').addClass ('ov_popup_list_item').appendTo (this.listDiv); + if (item.color) { + $('
').addClass ('ov_popup_list_item_rgbbox').css ('background', '#' + item.color).appendTo (listItemDiv); + } + $('
').addClass ('ov_popup_list_item_name').html (item.name).appendTo (listItemDiv); + listItemDiv.click (callbacks.onClick); + if (OV.IsHoverEnabled () && callbacks.onHoverStart && callbacks.onHoverStop) { + listItemDiv.hover ( + function () { + callbacks.onHoverStart (); + }, + function () { + callbacks.onHoverStop (); + } + ); + } + } + + Show () + { + this.modal.Open (); + } + + Hide () + { + this.modal.Close (); + } +}; + +OV.ShowMessageDialog = function (title, message, subMessage) +{ + let dialog = new OV.ButtonDialog (); + let contentDiv = dialog.Init (title, [ + { + name : 'OK', + onClick () { + dialog.Hide (); + } + } + ]); + $('
').addClass ('ov_dialog_message').html (message).appendTo (contentDiv); + if (subMessage !== null) { + $('
').addClass ('ov_dialog_submessage').html ('' + subMessage + '').appendTo (contentDiv); + } + dialog.Show (); + return dialog; +}; + +OV.ShowOpenUrlDialog = function (onOk) +{ + function CorrectFileHostUrls (urls) + { + for (let i = 0; i < urls.length; i++) { + let url = urls[i]; + if (url.search ('www.dropbox.com') !== -1) { + url = url.replace ('www.dropbox.com', 'dl.dropbox.com'); + let separatorPos = url.indexOf ('?'); + if (separatorPos !== -1) { + url = url.substr (0, separatorPos); + } + urls[i] = url; + } else if (url.search ('github.com') !== -1) { + url = url.replace ('github.com', 'raw.githubusercontent.com'); + url = url.replace ('/blob', ''); + let separatorPos = url.indexOf ('?'); + if (separatorPos !== -1) { + url = url.substr (0, separatorPos); + } + urls[i] = url; + } + } + } + + let dialog = new OV.ButtonDialog (); + let urlsTextArea = $('